diff --git a/examples.txt b/examples.txt index 1ad6eca..58af207 100644 --- a/examples.txt +++ b/examples.txt @@ -1,74 +1,74 @@ -Hello World -Типы данных (Values) -Переменные (Variables) -Константы (Constants) -Цикл For -If/Else -Switch -Массивы (Arrays) -Срезы (Slices) -Карты (Maps) -Ряд (Range) -Функции (Functions) -Функции с множественным возвратом (Multiple Return Values) -Функции с переменным числом аргументов (Variadic Functions) -Замыкания (Closures) -Рекурсия (Recursion) -Указатели (Pointers) -Структуры (Structs) -Методы (Methods) -Интерфейсы (Interfaces) -Ошибки (Errors) -Горутины (Goroutines) -Каналы (Channels) -Буферизированный канал (Channel Buffering) -Синхронизация канала (Channel Synchronization) -Направления канала (Channel Directions) -Select -Тайм-ауты (Timeouts) -Неблокируемые операции в каналах (Non-Blocking Channel Operations) -Закрытие каналов (Closing Channels) -Перебор значений из каналов (Range over Channels) -Таймеры (Timers) -Тикеры (повторения) (Tickers) -Пулы воркеров (Worker Pools) -WaitGroups -Ограничение скорости (Rate Limiting) -Атомарные счетчики (Atomic Counters) -Мьютексы (Mutexes) -Управление состоянием горутин (Stateful Goroutines) -Сортировка (Sorting) -Сортировка через функции (Sorting by Functions) -Panic -Defer -Функции коллекции (Collection Functions) -Строковые функции (String Functions) -Форматирование строк (String Formatting) -Регулярные выражения (Regular Expressions) -JSON -XML -Время (Time) -Epoch -Форматирование времени (Time Formatting / Parsing) -Случайные числа (Random Numbers) -Парсинг чисел (Number Parsing) -Парсинг URL (URL Parsing) -Хеш SHA1 (SHA1 Hashes) -Кодирование Base64 (Base64 Encoding) -Чтение файлов (Reading Files) -Запись файлов (Writing Files) -Строковые фильтры (Line Filters) -Пути к файлам (File Paths) -Директории (Directories) -Временные файлы и директории (Temporary Files and Directories) -Тестирование (Testing) -Аргументы командной строки (Command-Line Arguments) -Флаги командной строки (Command-Line Flags) -Подкоманды командной строки (Command-Line Subcommands) -Переменные среды (Environment Variables) -HTTP клиенты (HTTP Clients) -HTTP серверы (HTTP Servers) -Порождающие процессы (Spawning Processes) -Исполняющие процессы (Exec'ing Processes) -Сигналы (Signals) -Выход (Exit) +Hello World|Hello World +Типы данных (Values)|Values +Переменные (Variables)|Variables +Константы (Constants)|Constants +Цикл For|For +If/Else|If/Else +Switch|Switch +Массивы (Arrays)|Arrays +Срезы (Slices)|Slices +Карты (Maps)|Maps +Ряд (Range)|Range +Функции (Functions)|Functions +Функции с множественным возвратом (Multiple Return Values)|Multiple Return Values +Функции с переменным числом аргументов (Variadic Functions)|Variadic Functions +Замыкания (Closures)|Closures +Рекурсия (Recursion)|Recursion +Указатели (Pointers)|Pointers +Структуры (Structs)|Structs +Методы (Methods)|Methods +Интерфейсы (Interfaces)|Interfaces +Ошибки (Errors)|Errors +Горутины (Goroutines)|Goroutines +Каналы (Channels)|Channels +Буферизированный канал (Channel Buffering)|Channel Buffering +Синхронизация канала (Channel Synchronization)|Channel Synchronization +Направления канала (Channel Directions)|Channel Directions +Select|Select +Тайм-ауты (Timeouts)|Timeouts +Неблокируемые операции в каналах (Non-Blocking Channel Operations)|Non-Blocking Channel Operations +Закрытие каналов (Closing Channels)|Closing Channels +Перебор значений из каналов (Range over Channels)|Range over Channels +Таймеры (Timers)|Timers +Тикеры (повторения) (Tickers)|Tickers +Пулы воркеров (Worker Pools)|Worker Pools +WaitGroups|WaitGroups +Ограничение скорости (Rate Limiting)|Rate Limiting +Атомарные счетчики (Atomic Counters)|Atomic Counters +Мьютексы (Mutexes)|Mutexes +Управление состоянием горутин (Stateful Goroutines)|Stateful Goroutines +Сортировка (Sorting)|Sorting +Сортировка через функции (Sorting by Functions)|Sorting by Functions +Panic|Panic +Defer|Defer +Функции коллекции (Collection Functions)|Collection Functions +Строковые функции (String Functions)|String Functions +Форматирование строк (String Formatting)|String Formatting +Регулярные выражения (Regular Expressions)|Regular Expressions +JSON|JSON +XML|XML +Время (Time)|Time +Epoch|Epoch +Форматирование времени (Time Formatting / Parsing)|Time Formatting / Parsing +Случайные числа (Random Numbers)|Random Numbers +Парсинг чисел (Number Parsing)|Number Parsing +Парсинг URL (URL Parsing)|URL Parsing +Хеш SHA1 (SHA1 Hashes)|SHA1 Hashes +Кодирование Base64 (Base64 Encoding)|Base64 Encoding +Чтение файлов (Reading Files)|Reading Files +Запись файлов (Writing Files)|Writing Files +Строковые фильтры (Line Filters)|Line Filters +Пути к файлам (File Paths)|File Paths +Директории (Directories)|Directories +Временные файлы и директории (Temporary Files and Directories)|Temporary Files and Directories +Тестирование (Testing)|Testing +Аргументы командной строки (Command-Line Arguments)|Command-Line Arguments +Флаги командной строки (Command-Line Flags)|Command-Line Flags +Подкоманды командной строки (Command-Line Subcommands)|Command-Line Subcommands +Переменные среды (Environment Variables)|Environment Variables +HTTP клиенты (HTTP Clients)|HTTP Clients +HTTP серверы (HTTP Servers)|HTTP Servers +Порождающие процессы (Spawning Processes)|Spawning Processes +Исполняющие процессы (Exec'ing Processes)|Exec'ing Processes +Сигналы (Signals)|Signals +Выход (Exit)|Exit diff --git a/examples/.hash b/examples/.hash new file mode 100644 index 0000000..8ab57f4 --- /dev/null +++ b/examples/.hash @@ -0,0 +1,2 @@ +da39a3ee5e6b4b0d3255bfef95601890afd80709 +UCPdVNrl0-P diff --git a/examples/arrays/arrays.hash b/examples/arrays/arrays.hash index 8ab57f4..ad69e96 100644 --- a/examples/arrays/arrays.hash +++ b/examples/arrays/arrays.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +cbd7ae7158cdb62fbd0f5a21c1f89a9a43369900 +CiZ6oDF6A_l diff --git a/examples/atomic-counters/atomic-counters.hash b/examples/atomic-counters/atomic-counters.hash index 8ab57f4..d8d3864 100644 --- a/examples/atomic-counters/atomic-counters.hash +++ b/examples/atomic-counters/atomic-counters.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +e1b3a73458ef10d4cfe093766d05503ed7706544 +aEOYD0chCYy diff --git a/examples/base64-encoding/base64-encoding.hash b/examples/base64-encoding/base64-encoding.hash index 717ff3f..b5f4766 100644 --- a/examples/base64-encoding/base64-encoding.hash +++ b/examples/base64-encoding/base64-encoding.hash @@ -1,2 +1,2 @@ -e0148b9b4acb01e849b8f678cba03f549d250c44 -V3oV1bvh94k +0a1abdfdd647b24082d46fd54645bc597fb86b83 +3E068RAuW3p diff --git a/examples/channel-buffering/channel-buffering.hash b/examples/channel-buffering/channel-buffering.hash index 8ab57f4..b8c3cbb 100644 --- a/examples/channel-buffering/channel-buffering.hash +++ b/examples/channel-buffering/channel-buffering.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +4d4cf7b115f26c12d7e188a2d25c66e64f34edfe +HNVSsVpnHXN diff --git a/examples/channel-directions/channel-directions.hash b/examples/channel-directions/channel-directions.hash index 8ab57f4..700e311 100644 --- a/examples/channel-directions/channel-directions.hash +++ b/examples/channel-directions/channel-directions.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +a0904bf943d61e4d220f29ef3a0e6f1368657e78 +uEeOmen99CF diff --git a/examples/channel-synchronization/channel-synchronization.hash b/examples/channel-synchronization/channel-synchronization.hash index 8ab57f4..16fea5e 100644 --- a/examples/channel-synchronization/channel-synchronization.hash +++ b/examples/channel-synchronization/channel-synchronization.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +29e4099160217bd2817aef8546927056b9ea4b12 +3NHqwE7Bwjz diff --git a/examples/channels/channels.hash b/examples/channels/channels.hash index 8ab57f4..eac8a35 100644 --- a/examples/channels/channels.hash +++ b/examples/channels/channels.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +92a2a89118f08e7ac1fcf82567c14a31e6a2fabe +jHsjIHrpHXV diff --git a/examples/closing-channels/closing-channels.hash b/examples/closing-channels/closing-channels.hash index 8ab57f4..16da6f6 100644 --- a/examples/closing-channels/closing-channels.hash +++ b/examples/closing-channels/closing-channels.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +67493a491669c559ceac851a959828a00ceb89eb +V8RCD4w9KY- diff --git a/examples/closures/closures.hash b/examples/closures/closures.hash index 8ab57f4..104a80b 100644 --- a/examples/closures/closures.hash +++ b/examples/closures/closures.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +5a274ea5c01af8573ce5c6382f41189446a8be09 +J2PladF_bU9 diff --git a/examples/collection-functions/collection-functions.hash b/examples/collection-functions/collection-functions.hash index 8ab57f4..49e6fd6 100644 --- a/examples/collection-functions/collection-functions.hash +++ b/examples/collection-functions/collection-functions.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +5e8a32a022ffb4f746bbdf101face52ca008aeba +urAoHQtRHro diff --git a/examples/command-line-arguments/command-line-arguments.hash b/examples/command-line-arguments/command-line-arguments.hash index 8ab57f4..b81990d 100644 --- a/examples/command-line-arguments/command-line-arguments.hash +++ b/examples/command-line-arguments/command-line-arguments.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +375f50de2467743174f0d60d0635a3198e031094 +wSDrVn9TIK5 diff --git a/examples/command-line-flags/command-line-flags.hash b/examples/command-line-flags/command-line-flags.hash index 8ab57f4..2262d76 100644 --- a/examples/command-line-flags/command-line-flags.hash +++ b/examples/command-line-flags/command-line-flags.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +e316c57acf4cc993f182f2d341a608a1eac4a551 +_r_-ZzDzQUH diff --git a/examples/command-line-subcommands/command-line-subcommands.hash b/examples/command-line-subcommands/command-line-subcommands.hash index 8ab57f4..f695b3c 100644 --- a/examples/command-line-subcommands/command-line-subcommands.hash +++ b/examples/command-line-subcommands/command-line-subcommands.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +7320b5803ad9875733c4ca9644600ae8a55009d8 +sr1FH0P_2Te diff --git a/examples/constants/constants.hash b/examples/constants/constants.hash index 8ab57f4..9cb2151 100644 --- a/examples/constants/constants.hash +++ b/examples/constants/constants.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +31b56e63896203b29fd3a08a1f5036ca62d6d0c2 +e1Vlh7LNMFJ diff --git a/examples/directories/directories.hash b/examples/directories/directories.hash index 8ab57f4..fa8c641 100644 --- a/examples/directories/directories.hash +++ b/examples/directories/directories.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +7df99bb0443cbe61600e6466782a66e76cf7d927 +6lC_YCFEXVl diff --git a/examples/environment-variables/environment-variables.hash b/examples/environment-variables/environment-variables.hash index 8ab57f4..3177ea5 100644 --- a/examples/environment-variables/environment-variables.hash +++ b/examples/environment-variables/environment-variables.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +61c3cd2ec3ab8fcd4528452e88fe25c0eafbb2de +--fC2aTdXEz diff --git a/examples/errors/errors.hash b/examples/errors/errors.hash index 8ab57f4..7c79ad8 100644 --- a/examples/errors/errors.hash +++ b/examples/errors/errors.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +91979262128065a70899eb9ee32da1549864b609 +9-iENtmoI5k diff --git a/examples/execing-processes/execing-processes.hash b/examples/execing-processes/execing-processes.hash index 8ab57f4..d0dcce3 100644 --- a/examples/execing-processes/execing-processes.hash +++ b/examples/execing-processes/execing-processes.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +878cb29aa644aef60217a2d97a586080cd783daa +dojmebwCvC4 diff --git a/examples/exit/exit.hash b/examples/exit/exit.hash index 8ab57f4..af49b62 100644 --- a/examples/exit/exit.hash +++ b/examples/exit/exit.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +450a55230493ea17b3b7aeef666f55f796c050a8 +BgQ_ag62q4k diff --git a/examples/file-paths/file-paths.hash b/examples/file-paths/file-paths.hash index 8ab57f4..fd63161 100644 --- a/examples/file-paths/file-paths.hash +++ b/examples/file-paths/file-paths.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +7bbdffb4411e01e9962593d1de16a5654fb780d4 +6VDVplbM_Sk diff --git a/examples/for/for.hash b/examples/for/for.hash index 8ab57f4..ff069d6 100644 --- a/examples/for/for.hash +++ b/examples/for/for.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +c478d7d6259b800c42307c0d520fb669109cb2f7 +Fv7uI0y_EWr diff --git a/examples/functions/functions.hash b/examples/functions/functions.hash index 8ab57f4..742cfe2 100644 --- a/examples/functions/functions.hash +++ b/examples/functions/functions.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +fe343c61ebf68660290e3717592e7904c47abad5 +4sH5Tv_SBoU diff --git a/examples/goroutines/goroutines.hash b/examples/goroutines/goroutines.hash index 8ab57f4..ab46c61 100644 --- a/examples/goroutines/goroutines.hash +++ b/examples/goroutines/goroutines.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +9de125c9da86967491563075071ccd110b95b8ef +79i2V9dLU15 diff --git a/examples/http-clients/http-clients.hash b/examples/http-clients/http-clients.hash index e550dd5..8f7dd6a 100644 --- a/examples/http-clients/http-clients.hash +++ b/examples/http-clients/http-clients.hash @@ -1,2 +1,2 @@ -ec8fd69aa19e54a7ea05d2a911f09d3a98f0396c -VxYIifr_UuH +4efc6eb143a0e04132f16fcf929f7eed232b897d +8Z80D4rlbaE diff --git a/examples/http-servers/http-servers.hash b/examples/http-servers/http-servers.hash index a491ac1..553440b 100644 --- a/examples/http-servers/http-servers.hash +++ b/examples/http-servers/http-servers.hash @@ -1,2 +1,2 @@ -a4e8d30b7a6f3a6abd96b916d81ce5930bad94f9 -lNuS9ysZmxH +0282a23b343fbbbf9e3cb32d1efd934c9d707d26 +xndCIK5Wo-Y diff --git a/examples/interfaces/interfaces.hash b/examples/interfaces/interfaces.hash index 8ab57f4..e0bd9e8 100644 --- a/examples/interfaces/interfaces.hash +++ b/examples/interfaces/interfaces.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +e09278982d7d148dd22c45730d0820659a7ecae8 +bS2bkTzP8cG diff --git a/examples/line-filters/line-filters.hash b/examples/line-filters/line-filters.hash index 8ab57f4..298af0b 100644 --- a/examples/line-filters/line-filters.hash +++ b/examples/line-filters/line-filters.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +92a88a19fb7453935093e2ae893fcef18432f596 +dLiHA3PDCFp diff --git a/examples/maps/maps.hash b/examples/maps/maps.hash index 8ab57f4..3f261eb 100644 --- a/examples/maps/maps.hash +++ b/examples/maps/maps.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +df6205e7069d3914f2a40940696755f4b6343a9b +HjBZNUEcF_e diff --git a/examples/methods/methods.hash b/examples/methods/methods.hash index 8ab57f4..37ab1b3 100644 --- a/examples/methods/methods.hash +++ b/examples/methods/methods.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +b3128d2e4d2238e56d99bfc7138389257076871f +qfALObNYETK diff --git a/examples/multiple-return-values/multiple-return-values.hash b/examples/multiple-return-values/multiple-return-values.hash index 8ab57f4..9a8cf5c 100644 --- a/examples/multiple-return-values/multiple-return-values.hash +++ b/examples/multiple-return-values/multiple-return-values.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +9cfe1eb1a10a8977bee1581389953af18afc9bc0 +6Khmls9Dsin diff --git a/examples/mutexes/mutexes.hash b/examples/mutexes/mutexes.hash index 8ab57f4..501066e 100644 --- a/examples/mutexes/mutexes.hash +++ b/examples/mutexes/mutexes.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +8c7db363d911ef04d1340ff195da03a5373b79ab +USO1aTX_xMe diff --git a/examples/non-blocking-channel-operations/non-blocking-channel-operations.hash b/examples/non-blocking-channel-operations/non-blocking-channel-operations.hash index 8ab57f4..70fa503 100644 --- a/examples/non-blocking-channel-operations/non-blocking-channel-operations.hash +++ b/examples/non-blocking-channel-operations/non-blocking-channel-operations.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +3b01f109970c31ffb1c67cfea86a8eaa2959f7e0 +FokCtF5TvgL diff --git a/examples/number-parsing/number-parsing.hash b/examples/number-parsing/number-parsing.hash index 8ab57f4..40ced04 100644 --- a/examples/number-parsing/number-parsing.hash +++ b/examples/number-parsing/number-parsing.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +9b4a9b30f02201adf86ab6c6abc60e23088026be +4PKUrIjNyTE diff --git a/examples/pointers/pointers.hash b/examples/pointers/pointers.hash index 8ab57f4..69d7ef2 100644 --- a/examples/pointers/pointers.hash +++ b/examples/pointers/pointers.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +909a2f7a79ef942a544292b283a964ae530fd612 +VHnMkOQfQea diff --git a/examples/random-numbers/random-numbers.hash b/examples/random-numbers/random-numbers.hash index 8ab57f4..acd1836 100644 --- a/examples/random-numbers/random-numbers.hash +++ b/examples/random-numbers/random-numbers.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +03975452ff4c127a4a78897643ce3eca0b90b239 +CKeOTktHnJe diff --git a/examples/range-over-channels/range-over-channels.hash b/examples/range-over-channels/range-over-channels.hash index 8ab57f4..0200e30 100644 --- a/examples/range-over-channels/range-over-channels.hash +++ b/examples/range-over-channels/range-over-channels.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +a2f106b835828ac784feffb869604e059d788680 +zbl7ytLnFCK diff --git a/examples/range/range.hash b/examples/range/range.hash index 8ab57f4..a1df701 100644 --- a/examples/range/range.hash +++ b/examples/range/range.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +f8c6ffeaa1f52aeff136779442bbd160313a6b32 +4GBukIe-7ie diff --git a/examples/rate-limiting/rate-limiting.hash b/examples/rate-limiting/rate-limiting.hash index 8ab57f4..19e0c2c 100644 --- a/examples/rate-limiting/rate-limiting.hash +++ b/examples/rate-limiting/rate-limiting.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +a93813bfc920c685c55bf09a3425f63fcc4a542b +rC0qLhImKAo diff --git a/examples/reading-files/reading-files.hash b/examples/reading-files/reading-files.hash index 8ab57f4..24886b8 100644 --- a/examples/reading-files/reading-files.hash +++ b/examples/reading-files/reading-files.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +176b57caf5a053e3707da299ca9fbb78314d283f +m6mvXlZtHTX diff --git a/examples/recursion/recursion.hash b/examples/recursion/recursion.hash index 8ab57f4..26a2e36 100644 --- a/examples/recursion/recursion.hash +++ b/examples/recursion/recursion.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +b75f7597af0526d0fefb481ef7987b5e186c0c04 +oPbHEHLaRrN diff --git a/examples/regular-expressions/regular-expressions.hash b/examples/regular-expressions/regular-expressions.hash index 8ab57f4..fbd8efc 100644 --- a/examples/regular-expressions/regular-expressions.hash +++ b/examples/regular-expressions/regular-expressions.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +984c5939e46284833637df8aa8dd38f51b571425 +GB_bgVcRKg2 diff --git a/examples/sha1-hashes/sha1-hashes.hash b/examples/sha1-hashes/sha1-hashes.hash index 5af2939..b69d352 100644 --- a/examples/sha1-hashes/sha1-hashes.hash +++ b/examples/sha1-hashes/sha1-hashes.hash @@ -1,2 +1,2 @@ -4cda643ba233014fe6b30966c37d4d0fcd4edbe8 -oqcrTfY4Ykd +f64d96d9e998d1c8821689af70580b3929a464cf +zhhfA6LqgFI diff --git a/examples/signals/signals.hash b/examples/signals/signals.hash index 8ab57f4..02bac3b 100644 --- a/examples/signals/signals.hash +++ b/examples/signals/signals.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +8e3867a11504ce30880c75e3d5ed74907a8ef1b5 +Xs9zJ53YIAa diff --git a/examples/slices/slices.hash b/examples/slices/slices.hash index 8ab57f4..a4ad5e8 100644 --- a/examples/slices/slices.hash +++ b/examples/slices/slices.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +97236c6deebee661d0bd1e696b12e4ea671d06ff +32lRsL-MYrP diff --git a/examples/sorting-by-functions/sorting-by-functions.hash b/examples/sorting-by-functions/sorting-by-functions.hash index 8ab57f4..374e267 100644 --- a/examples/sorting-by-functions/sorting-by-functions.hash +++ b/examples/sorting-by-functions/sorting-by-functions.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +0252f365e73e310a8f4dc4431f598ffcea3efc72 +VQrgvKKcvRg diff --git a/examples/sorting/sorting.hash b/examples/sorting/sorting.hash index 8ab57f4..c28118f 100644 --- a/examples/sorting/sorting.hash +++ b/examples/sorting/sorting.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +a059f86c91ed4084fb37f321a7def4ef88b62e67 +X9bT-mDzj4o diff --git a/examples/spawning-processes/spawning-processes.hash b/examples/spawning-processes/spawning-processes.hash index 8ab57f4..2ccab24 100644 --- a/examples/spawning-processes/spawning-processes.hash +++ b/examples/spawning-processes/spawning-processes.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +4984e90d62323e378c35e5c52d0c5646fd150d82 +2QNUQPuiFlR diff --git a/examples/stateful-goroutines/stateful-goroutines.hash b/examples/stateful-goroutines/stateful-goroutines.hash index 8ab57f4..bb70ee5 100644 --- a/examples/stateful-goroutines/stateful-goroutines.hash +++ b/examples/stateful-goroutines/stateful-goroutines.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +e7e07404454809cbacb719ad94e70726d22dacc1 +n_gVZus4DBP diff --git a/examples/string-formatting/string-formatting.hash b/examples/string-formatting/string-formatting.hash index 8ab57f4..a8fe8ea 100644 --- a/examples/string-formatting/string-formatting.hash +++ b/examples/string-formatting/string-formatting.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +8ffedfce40621f09c42f07459127279d66425d08 +TKe8eifne_2 diff --git a/examples/string-functions/string-functions.hash b/examples/string-functions/string-functions.hash index 8ab57f4..e720ebb 100644 --- a/examples/string-functions/string-functions.hash +++ b/examples/string-functions/string-functions.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +818faf1a5caccb1628010427eff861edd8379de8 +UeDwSoYepQt diff --git a/examples/structs/structs.hash b/examples/structs/structs.hash index 8ab57f4..f0189fa 100644 --- a/examples/structs/structs.hash +++ b/examples/structs/structs.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +995a3c5dec1de68fe79d5d3172531fb5d2b99870 +sJZCI3Gm3Ht diff --git a/examples/temporary-files-and-directories/temporary-files-and-directories.hash b/examples/temporary-files-and-directories/temporary-files-and-directories.hash index 8ab57f4..e68654d 100644 --- a/examples/temporary-files-and-directories/temporary-files-and-directories.hash +++ b/examples/temporary-files-and-directories/temporary-files-and-directories.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +99866fef55916b593bf73df9591ca9d7d2c86d7e +PMJP7pf-n5h diff --git a/examples/testing/testing.hash b/examples/testing/testing.hash index 8ab57f4..3f809e1 100644 --- a/examples/testing/testing.hash +++ b/examples/testing/testing.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +1a41d8c725f8bfe62838744d6327404ec1ddc319 +gZXCCqO7ezT diff --git a/examples/tickers/tickers.hash b/examples/tickers/tickers.hash index 8ab57f4..b4fefad 100644 --- a/examples/tickers/tickers.hash +++ b/examples/tickers/tickers.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +64772226ab38092e29f23802c8138a723e23cc83 +2bpsxisGGLj diff --git a/examples/time-formatting-parsing/time-formatting-parsing.hash b/examples/time-formatting-parsing/time-formatting-parsing.hash index 8ab57f4..892e30e 100644 --- a/examples/time-formatting-parsing/time-formatting-parsing.hash +++ b/examples/time-formatting-parsing/time-formatting-parsing.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +7a8299db21f9554a2b43ecece2d8abf7a370189c +G61ruGfMQZA diff --git a/examples/time/time.hash b/examples/time/time.hash index 8ab57f4..15db181 100644 --- a/examples/time/time.hash +++ b/examples/time/time.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +e532cf20c51b0106aba8c041ec44480f7690da5e +ZE4n1Jt55m0 diff --git a/examples/timeouts/timeouts.hash b/examples/timeouts/timeouts.hash index 8ab57f4..91d44dc 100644 --- a/examples/timeouts/timeouts.hash +++ b/examples/timeouts/timeouts.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +441dc07d57c03df070488f85dbe24e5ef6591bf7 +SCMyPsGlXtX diff --git a/examples/timers/timers.hash b/examples/timers/timers.hash index 8ab57f4..87c1ff7 100644 --- a/examples/timers/timers.hash +++ b/examples/timers/timers.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +d1689f059a29e7e0d3f9ccd52632220d487d6ca0 +7RnBhz_kuki diff --git a/examples/url-parsing/url-parsing.hash b/examples/url-parsing/url-parsing.hash index 77021f1..28c5bcd 100644 --- a/examples/url-parsing/url-parsing.hash +++ b/examples/url-parsing/url-parsing.hash @@ -1,2 +1,2 @@ -babc12f5066652f4cb0151231c06f1037298ff28 -M218D9Tldlr +f0b0a50b3471f972b26e6fefb481730661aac6e1 +Zt1KHMGZESo diff --git a/examples/values/values.hash b/examples/values/values.hash index 8ab57f4..3716621 100644 --- a/examples/values/values.hash +++ b/examples/values/values.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +1c4cf252844f42861e8fbc59a604833d0dbd24c0 +SYLIv-rApFj diff --git a/examples/variables/variables.hash b/examples/variables/variables.hash index 8ab57f4..76bfeee 100644 --- a/examples/variables/variables.hash +++ b/examples/variables/variables.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +e5f981f078207ac4f67b26be87a73c2989006685 +BUbDhWP8PV_k diff --git a/examples/variadic-functions/variadic-functions.hash b/examples/variadic-functions/variadic-functions.hash index 8ab57f4..46c87a0 100644 --- a/examples/variadic-functions/variadic-functions.hash +++ b/examples/variadic-functions/variadic-functions.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +e32d27f8b78c3e3f24c5dd98f864ffdb9567d628 +dz6tVXvffuH diff --git a/examples/worker-pools/worker-pools.hash b/examples/worker-pools/worker-pools.hash index 8ab57f4..fc92632 100644 --- a/examples/worker-pools/worker-pools.hash +++ b/examples/worker-pools/worker-pools.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +a719d863a0b07434beade5c6582eff76df8bd2ef +79qTVPHnwGQ diff --git a/examples/writing-files/writing-files.hash b/examples/writing-files/writing-files.hash index 8ab57f4..89cec11 100644 --- a/examples/writing-files/writing-files.hash +++ b/examples/writing-files/writing-files.hash @@ -1,2 +1,2 @@ -da39a3ee5e6b4b0d3255bfef95601890afd80709 -UCPdVNrl0-P +f6c74f9903720805417ffe5765cef04e3f58d966 +LscGbwmQZns diff --git a/public/arrays b/public/arrays index d19bf1c..caf712a 100644 --- a/public/arrays +++ b/public/arrays @@ -23,6 +23,179 @@

Go в примерах: Массивы (Arrays)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

В Go, массив это числовой ряд элементов определенной +длины.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+ + + +
func main() {
+
+ +
+

В данном примере мы создаем массив a, который +содержит 5 элементов с типом int. Тип элементов +и длина являются частью типа массива. По-умолчанию +массив заполняется нулевыми значениями, например, +в случае int нулевое значение - 0.

+ +
+ +
    var a [5]int
+    fmt.Println("emp:", a)
+
+ +
+

Мы можем установить значение по индексу элемента +следующим образом:array[index] = value. +Получить значение можно аналогично - array[index].

+ +
+ +
    a[4] = 100
+    fmt.Println("set:", a)
+    fmt.Println("get:", a[4])
+
+ +
+

Встроенная функция len возвращает длину массива.

+ +
+ +
    fmt.Println("len:", len(a))
+
+ +
+

Так можно инициалзировать и заполнить массив +значениеми в одну строку

+ +
+ +
    b := [5]int{1, 2, 3, 4, 5}
+    fmt.Println("dcl:", b)
+
+ +
+

Тип массив является одномерным. Но вы можете +совмещать типы, для создания многомерных +структур.

+ +
+ +
    var twoD [2][3]int
+    for i := 0; i < 2; i++ {
+        for j := 0; j < 3; j++ {
+            twoD[i][j] = i + j
+        }
+    }
+    fmt.Println("2d: ", twoD)
+}
+
+ +
+ + + + + + + + + + + + + +
+

Обратите внимание, что массивы отображаются в виде +[v1 v2 v3 …] при выводе с помощью fmt.Println.

+ +
+ +
$ go run arrays.go
+emp: [0 0 0 0 0]
+set: [0 0 0 0 100]
+get: 100
+len: 5
+dcl: [1 2 3 4 5]
+2d:  [[0 1 2] [1 2 3]]
+
+ +
+

В Go вы будете встречать срезы гораздо чаще, чем +массивы. Срезы рассмотрим далее

+ +
+ + +
+

Следующий пример: Срезы (Slices). @@ -34,7 +207,7 @@

diff --git a/public/atomic-counters b/public/atomic-counters index e073b4b..d3775c0 100644 --- a/public/atomic-counters +++ b/public/atomic-counters @@ -23,6 +23,206 @@

Go в примерах: Атомарные счетчики (Atomic Counters)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Основным механизмом управления состоянием в Go является +связь по каналам. Мы видели это, например, с пулами воркеров. +Есть несколько других вариантов управления состоянием. +Здесь мы рассмотрим использование пакета sync/atomic +для атомарных счетчиков, к которым обращаются горутины.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "sync"
+    "sync/atomic"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Мы будем использовать целое число без знака +для представления нашего (всегда положительного) +счетчика.

+ +
+ +
    var ops uint64
+
+ +
+

WaitGroup поможет нам подождать, пока все горутины +завершат свою работу.

+ +
+ +
    var wg sync.WaitGroup
+
+ +
+

Мы запустим 50 горутин, каждая из которых увеличивает +счетчик ровно в 1000 раз.

+ +
+ +
    for i := 0; i < 50; i++ {
+        wg.Add(1)
+
+ +
+

Для атомарного увеличения счетчика мы +используем AddUint64, присваивая ему адрес +памяти нашего счетчика ops с префиксом &.

+ +
+ +
        go func() {
+            for c := 0; c < 1000; c++ {
+
+ +
+ + + +
                atomic.AddUint64(&ops, 1)
+            }
+            wg.Done()
+        }()
+    }
+
+ +
+

Ждем пока завершатся горутины.

+ +
+ +
    wg.Wait()
+
+ +
+

Теперь доступ к ops безопасен, потому что мы знаем, +что никакие другие горутины не пишут в него. Безопасное +чтение атомарного счетчика во время его обновления также +возможно, используя функцию atomic.LoadUint64.

+ +
+ +
    fmt.Println("ops:", ops)
+}
+
+ +
+ + + + + + + + + + + + + +
+

Мы ожидаем получить ровно 50 000 операций. Если бы +мы использовали неатомарный ops++ для увеличения +счетчика, мы бы, вероятно, получили другое число, +изменяющееся между прогонами, потому что горутины +мешали бы друг другу. Более того, мы получим сбои +в гонке данных при работе с флагом -race.

+ +
+ +
$ go run atomic-counters.go
+ops: 50000
+
+ +
+

Далее мы рассмотрим мьютексы, еще один способ +управления состоянием.

+ +
+ + +
+

Следующий пример: Мьютексы (Mutexes). @@ -34,7 +234,7 @@

diff --git a/public/base64 b/public/base64 deleted file mode 100644 index eadbd1c..0000000 --- a/public/base64 +++ /dev/null @@ -1,41 +0,0 @@ - - - - - Go в примерах: Кодирование Base64 (Base64 Encoding) - - - - -
-

Go в примерах: Кодирование Base64 (Base64 Encoding)

- - -

- Следующий пример: Чтение файлов (Reading Files). -

- - -
- - - - diff --git a/public/base64-encoding b/public/base64-encoding new file mode 100644 index 0000000..232a987 --- /dev/null +++ b/public/base64-encoding @@ -0,0 +1,199 @@ + + + + + Go в примерах: Кодирование Base64 (Base64 Encoding) + + + + +
+

Go в примерах: Кодирование Base64 (Base64 Encoding)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Go имеет встроенную поддержку base64 +кодирования и декодирования.

+ +
+ + +
+ + + +
package main
+
+ +
+

Этот синтаксис импортирует пакет encoding/base64 с +с алиасом b64, вместо названия по-умолчанию. Это +сэкономит нам немного места.

+ +
+ +
import (
+    b64 "encoding/base64"
+    "fmt"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Строка, которую мы будем кодировать/декодировать.

+ +
+ +
    data := "abc123!?$*&()'-=@~"
+
+ +
+

Go поддерживает оба стандарта и URL-совместимого +base64. Кодируем, используя стандартнай кодировщик. +Кодировщик требует []byte на входе, поэтому +мы конвертируем нашу строку.

+ +
+ +
    sEnc := b64.StdEncoding.EncodeToString([]byte(data))
+    fmt.Println(sEnc)
+
+ +
+

Декодирование может вернуть ошибку, которую можно +проверить, если вы не уверены в корректности +входных данных.

+ +
+ +
    sDec, _ := b64.StdEncoding.DecodeString(sEnc)
+    fmt.Println(string(sDec))
+    fmt.Println()
+
+ +
+

Это кодирование/декодирование использует +URL-совместимый base64 формат.

+ +
+ +
    uEnc := b64.URLEncoding.EncodeToString([]byte(data))
+    fmt.Println(uEnc)
+    uDec, _ := b64.URLEncoding.DecodeString(uEnc)
+    fmt.Println(string(uDec))
+}
+
+ +
+ + + + + + + + + + + + + +
+

Строка кодируется в слегка отличающиеся значения с +помощью стандартных и URL-совместимые base64 +(+ vs -), но они оба декодируются в исходную +строку по желанию.

+ +
+ +
$ go run base64-encoding.go
+YWJjMTIzIT8kKiYoKSctPUB+
+abc123!?$*&()'-=@~
+
+ +
+ + + +
YWJjMTIzIT8kKiYoKSctPUB-
+abc123!?$*&()'-=@~
+
+ +
+ + +

+ Следующий пример: Чтение файлов (Reading Files). +

+ + +
+ + + + diff --git a/public/channel-buffering b/public/channel-buffering index 36fd138..6cbfdc6 100644 --- a/public/channel-buffering +++ b/public/channel-buffering @@ -23,6 +23,125 @@

Go в примерах: Буферизированный канал (Channel Buffering)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

По умолчанию каналы не буферизованы, это означает, +что они будут принимать отправления (chan <-), только +если есть соответствующий прием (<- chan), готовый +принять отправленное значение. Буферизованные каналы +принимают ограниченное количество значений без +соответствующего приемника для этих значений.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+ + + +
func main() {
+
+ +
+

Здесь мы создаем канал строк с буфером до 2 +значений.

+ +
+ +
    messages := make(chan string, 2)
+
+ +
+

Т.к. этот канал буферизирован, мы можем послать +значения в канал без соответствующего одновременного +получения.

+ +
+ +
    messages <- "buffered"
+    messages <- "channel"
+
+ +
+

Позже мы можем получить эти значения как обычно.

+ +
+ +
    fmt.Println(<-messages)
+    fmt.Println(<-messages)
+}
+
+ +
+ + + + + + + + +
+ + + +
$ go run channel-buffering.go 
+buffered
+channel
+
+ +
+

Следующий пример: Синхронизация канала (Channel Synchronization). @@ -34,7 +153,7 @@

diff --git a/public/channel-directions b/public/channel-directions index f50b677..11d92a8 100644 --- a/public/channel-directions +++ b/public/channel-directions @@ -23,6 +23,117 @@

Go в примерах: Направления канала (Channel Directions)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

При использовании каналов в качестве параметров +функции вы можете указать, предназначен ли канал +только для отправки или получения значений. Эта +возможность повышает безопасность программы.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+

Функция ping принимает канал только для отправки +значений. При попытке получения значений в этот канал +в процессе компиляции возниканет ошибка.

+ +
+ +
func ping(pings chan<- string, msg string) {
+    pings <- msg
+}
+
+ +
+

Функция pong принимает один канал для приема +(pings) и второй для отправки (pongs).

+ +
+ +
func pong(pings <-chan string, pongs chan<- string) {
+    msg := <-pings
+    pongs <- msg
+}
+
+ +
+ + + +
func main() {
+    pings := make(chan string, 1)
+    pongs := make(chan string, 1)
+    ping(pings, "passed message")
+    pong(pings, pongs)
+    fmt.Println(<-pongs)
+}
+
+ +
+ + + + + + + + +
+ + + +
$ go run channel-directions.go
+passed message
+
+ +
+

Следующий пример: Select. @@ -34,7 +145,7 @@

diff --git a/public/channel-synchronization b/public/channel-synchronization index b421afc..c7eadbc 100644 --- a/public/channel-synchronization +++ b/public/channel-synchronization @@ -23,6 +23,157 @@

Go в примерах: Синхронизация канала (Channel Synchronization)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Мы можем использовать каналы для синхронизации +выполнения между горутинами. Вот пример +использования блокирующего получения для ожидания +завершения работы горутины. При ожидании завершения +нескольких процедур вы можете использовать WaitGroup.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "time"
+)
+
+ +
+

Эту функцию мы будем запускать в горутине. Канал +done будет использован для оповещения другой +горутины о том, что функция выполнена успешно.

+ +
+ +
func worker(done chan bool) {
+    fmt.Print("working...")
+    time.Sleep(time.Second)
+    fmt.Println("done")
+
+ +
+

Отправить значение, чтобы сказать что функция +выполнена успешно.

+ +
+ +
    done <- true
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Запустите воркера в горутине и передайте ему канал +для оповещения.

+ +
+ +
    done := make(chan bool, 1)
+    go worker(done)
+
+ +
+

Блокируйте, пока мы не получим уведомление от +воркера из канала.

+ +
+ +
    <-done
+}
+
+ +
+ + + + + + + + + + + + + +
+ + + +
$ go run channel-synchronization.go      
+working...done                  
+
+ +
+

Если вы удалите строку <- done из этой программы, +программа закроется до того, как воркер даже +запустится.

+ +
+ + +
+

Следующий пример: Направления канала (Channel Directions). @@ -34,7 +185,7 @@

diff --git a/public/channels b/public/channels index 9711b17..f6be4e0 100644 --- a/public/channels +++ b/public/channels @@ -23,6 +23,140 @@

Go в примерах: Каналы (Channels)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Каналы это способ связи паралелльных горутин между +собой. Вы можете послать сообщение в канал из одной +горутины и получить его в другой.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+ + + +
func main() {
+
+ +
+

Создаем новый канал - make(chan val-type). +Каналы типизируются в зависимости от значений, +которые они передают.

+ +
+ +
    messages := make(chan string)
+
+ +
+

Отправьте значение в канал, используя channel <-. +Здесь мы отправляем "ping" в канал messages, +который мы сделали выше, из новой горутины.

+ +
+ +
    go func() { messages <- "ping" }()
+
+ +
+

Синтаксис <-channel читает из канала. Здесь +мы получим сообщение "ping", которое мы +отправили выше, и распечатаем его.

+ +
+ +
    msg := <-messages
+    fmt.Println(msg)
+}
+
+ +
+ + + + + + + + + + + + + +
+

Когда мы запускаем программу, сообщение "ping" успешно +передается из одной горутины в другую по нашему каналу.

+ +
+ +
$ go run channels.go 
+ping
+
+ +
+

По-умолчанию, отправление и получение блокируются, +пока оба отправителя и получателя готовы. Это +свойство позволило нам ждать в конце нашей программы +сообщения "ping" без использования какой-либо +другой синхронизации.

+ +
+ + +
+

Следующий пример: Буферизированный канал (Channel Buffering). @@ -34,7 +168,7 @@

diff --git a/public/closing-channels b/public/closing-channels index fa1c91c..fccc04b 100644 --- a/public/closing-channels +++ b/public/closing-channels @@ -23,6 +23,166 @@

Go в примерах: Закрытие каналов (Closing Channels)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Закрытие канала означает, что по нему больше не +будет отправлено никаких значений. Это может быть +полезно для сообщения получателям о завершении.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+

В этом примере мы будем использовать канал jobs для +передачи задания, которое должна быть выполнена из +main() в горутине. Когда у нас больше не будет +заданий для воркера, мы закроем канал jobs.

+ +
+ +
func main() {
+    jobs := make(chan int, 5)
+    done := make(chan bool)
+
+ +
+

Вот наш воркер. Он многократно получает из канала +jobs значения j, more := <-jobs. В этой специальной +форме получения с двумя значениями more значение +будет ложным, если jobs были закрыты, а все +значения в канале уже получены. Мы используем +это, чтобы уведомить о выполнении, когда мы +проработали все наши работы.

+ +
+ +
    go func() {
+        for {
+            j, more := <-jobs
+            if more {
+                fmt.Println("received job", j)
+            } else {
+                fmt.Println("received all jobs")
+                done <- true
+                return
+            }
+        }
+    }()
+
+ +
+

Отправляем 3 сообщения в канал jobs, и закрываем +его.

+ +
+ +
    for j := 1; j <= 3; j++ {
+        jobs <- j
+        fmt.Println("sent job", j)
+    }
+    close(jobs)
+    fmt.Println("sent all jobs")
+
+ +
+

Мы ожидаем выполнения всех каналов используя +синхронизацию, рассмотренную +нами ранее.

+ +
+ +
    <-done
+}
+
+ +
+ + + + + + + + + + + + + +
+ + + +
$ go run closing-channels.go 
+sent job 1
+received job 1
+sent job 2
+received job 2
+sent job 3
+received job 3
+sent all jobs
+received all jobs
+
+ +
+

Идея закрытых каналов естественно приводит нас к +следующему примеру: range по каналам.

+ +
+ + +
+

Следующий пример: Перебор значений из каналов (Range over Channels). @@ -34,7 +194,7 @@

diff --git a/public/closures b/public/closures index 1d3cbc4..a2bc20c 100644 --- a/public/closures +++ b/public/closures @@ -23,6 +23,162 @@

Go в примерах: Замыкания (Closures)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Go поддерживает анонимные функции, которые могут образовывать +замыкания. Анонимные функции полезны, когда вы хотите +определить встроенную функцию, не называя ее.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+

Эта функция intSeq возвращает другую функцию, которую +мы анонимно определяем в теле intSeq. Возвращенная +функция присваивается в переменную i, чтобы +сформировать замыкание.

+ +
+ +
func intSeq() func() int {
+    i := 0
+    return func() int {
+        i++
+        return i
+    }
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Мы вызываем intSeq, присваивая результат (функцию) +nextInt. Это значение функции фиксирует свое +собственное значение i, которое будет обновляться +каждый раз, когда мы вызываем nextInt.

+ +
+ +
    nextInt := intSeq()
+
+ +
+

Посмотрите, что происходит при вызове nextInt +несколько раз.

+ +
+ +
    fmt.Println(nextInt())
+    fmt.Println(nextInt())
+    fmt.Println(nextInt())
+
+ +
+

Чтобы подтвердить, что состояние является уникальным +для этой конкретной функции, создайте и протестируйте +новую.

+ +
+ +
    newInts := intSeq()
+    fmt.Println(newInts())
+}
+
+ +
+ + + + + + + + + + + + + +
+ + + +
$ go run closures.go
+1
+2
+3
+1
+
+ +
+

Последняя особенность функций, которые мы сейчас +рассмотрим, - это рекурсия.

+ +
+ + +
+

Следующий пример: Рекурсия (Recursion). @@ -34,7 +190,7 @@

diff --git a/public/collection-functions b/public/collection-functions index 9efcde8..afc3d5a 100644 --- a/public/collection-functions +++ b/public/collection-functions @@ -23,6 +23,343 @@

Go в примерах: Функции коллекции (Collection Functions)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Нам часто нужны наши программы для выполнения операций +с коллекциями данных, таких как выбор всех элементов, +которые удовлетворяют данному предикату, или отображение +всех элементов в новую коллекцию с помощью +пользовательской функции.

+ +
+ + +
+

В некоторых языках идиоматично использовать дженерики +и алгоритмы. Go не поддерживает дженерики; в Go, как +правило, предоставляют функции коллекции если они +необходимы конкретно для вашей программы и ваших +типов данных.

+ +
+ + +
+

Вот несколько примеров функций коллекции для срезов +со строковыми значениями. Вы можете использовать эти +примеры, чтобы сделать собственные функции. Обратите +внимание, что в некоторых случаях, возможно, было бы +более явным встроить код, манипулирующий коллекциями, +вместо создания создания вспомогательных функций.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "strings"
+)
+
+ +
+

Возвращает первый индекс совпадения со строкой t или -1 +если совпадение не найдено.

+ +
+ +
func Index(vs []string, t string) int {
+    for i, v := range vs {
+        if v == t {
+            return i
+        }
+    }
+    return -1
+}
+
+ +
+

Возвращает true если строка t находится в срезе.

+ +
+ +
func Include(vs []string, t string) bool {
+    return Index(vs, t) >= 0
+}
+
+ +
+

Возвращает true если одна из строк в срезе +удовлетворяет условию f.

+ +
+ +
func Any(vs []string, f func(string) bool) bool {
+    for _, v := range vs {
+        if f(v) {
+            return true
+        }
+    }
+    return false
+}
+
+ +
+

Возвращает true если все из строк в срезе +удовлетворяют условие f.

+ +
+ +
func All(vs []string, f func(string) bool) bool {
+    for _, v := range vs {
+        if !f(v) {
+            return false
+        }
+    }
+    return true
+}
+
+ +
+

Возвращает новый срез, содержащий все строки в срезе, +которые удовлетворяют условие f.

+ +
+ +
func Filter(vs []string, f func(string) bool) []string {
+    vsf := make([]string, 0)
+    for _, v := range vs {
+        if f(v) {
+            vsf = append(vsf, v)
+        }
+    }
+    return vsf
+}
+
+ +
+

Возвращает новый срез, содержащий результаты выполнения +функции f с каждой строкой исходного слайса.

+ +
+ +
func Map(vs []string, f func(string) string) []string {
+    vsm := make([]string, len(vs))
+    for i, v := range vs {
+        vsm[i] = f(v)
+    }
+    return vsm
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Здесь мы пробуем различные функции коллекций.

+ +
+ +
    var strs = []string{"peach", "apple", "pear", "plum"}
+
+ +
+ + + +
    fmt.Println(Index(strs, "pear"))
+
+ +
+ + + +
    fmt.Println(Include(strs, "grape"))
+
+ +
+ + + +
    fmt.Println(Any(strs, func(v string) bool {
+        return strings.HasPrefix(v, "p")
+    }))
+
+ +
+ + + +
    fmt.Println(All(strs, func(v string) bool {
+        return strings.HasPrefix(v, "p")
+    }))
+
+ +
+ + + +
    fmt.Println(Filter(strs, func(v string) bool {
+        return strings.Contains(v, "e")
+    }))
+
+ +
+

Все примеры, приведенные выше, использовали +анонимные функции, но вы можете использовать именные +функции корректного типа.

+ +
+ +
    fmt.Println(Map(strs, strings.ToUpper))
+
+ +
+ + + +
}
+
+ +
+ + + + + + + + +
+ + + +
$ go run collection-functions.go 
+2
+false
+true
+false
+[peach apple pear]
+[PEACH APPLE PEAR PLUM]
+
+ +
+

Следующий пример: Строковые функции (String Functions). @@ -34,7 +371,7 @@

diff --git a/public/command-line-arguments b/public/command-line-arguments index d3857df..988b8d7 100644 --- a/public/command-line-arguments +++ b/public/command-line-arguments @@ -23,6 +23,146 @@

Go в примерах: Аргументы командной строки (Command-Line Arguments)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Аргументы командной строки +являются распространенным способом параметризации +выполнения программ. Например, go run hello.go +использует аргументы run и hello.go для программы +go.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "os"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

os.Args предоставляет доступ к необработанным +аргументам командной строки. Обратите внимание, +что первое значение в этом срезе - это путь к +программе, а os.Args [1:] содержит аргументы +программы.

+ +
+ +
    argsWithProg := os.Args
+    argsWithoutProg := os.Args[1:]
+
+ +
+

Вы можете получить отдельные аргументы с +обычной индексацией.

+ +
+ +
    arg := os.Args[3]
+
+ +
+ + + +
    fmt.Println(argsWithProg)
+    fmt.Println(argsWithoutProg)
+    fmt.Println(arg)
+}
+
+ +
+ + + + + + + + + + + + + +
+

Чтобы поэкспериментировать с аргументами командной +строки, лучше сначала создать двоичный файл с +помощью go build.

+ +
+ +
$ go build command-line-arguments.go
+$ ./command-line-arguments a b c d
+[./command-line-arguments a b c d]       
+[a b c d]
+c
+
+ +
+

Далее мы рассмотрим более сложную обработку командной +строки с флагами.

+ +
+ + +
+

Следующий пример: Флаги командной строки (Command-Line Flags). @@ -34,7 +174,7 @@

diff --git a/public/command-line-flags b/public/command-line-flags index 97d6350..2263aea 100644 --- a/public/command-line-flags +++ b/public/command-line-flags @@ -23,6 +23,287 @@

Go в примерах: Флаги командной строки (Command-Line Flags)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Флаги командной строки +являются распространенным способом задания параметров +для программ командной строки. Например, в wc -l - -l +является флагом командной строки.

+ +
+ + +
+ + + +
package main
+
+ +
+

Go предоставляет пакет flag, поддерживающий базовый +парсинг флагов командной строки. Мы будем использовать +этот пакет для реализации нашего примера программы +командной строки.

+ +
+ +
import (
+    "flag"
+    "fmt"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Основные объявления флагов доступны для строковых, +целочисленных и логических параметров. Здесь мы +объявляем строковой флаг word со значением по +умолчанию "foo" и кратким описанием. Функция +flag.String возвращает строковый указатель (не +строковое значение); мы увидим, как использовать +этот указатель ниже.

+ +
+ +
    wordPtr := flag.String("word", "foo", "a string")
+
+ +
+

Объявляем флаги numb и fork, используя тот же +подход, что и выше.

+ +
+ +
    numbPtr := flag.Int("numb", 42, "an int")
+    boolPtr := flag.Bool("fork", false, "a bool")
+
+ +
+

Также возможно вызвать метод, который использует +существующую переменную, объявленную в другом месте +программы. Обратите внимание, что в данном случае +необходимо передать указатель.

+ +
+ +
    var svar string
+    flag.StringVar(&svar, "svar", "bar", "a string var")
+
+ +
+

Как только все флаги объявлены, вызовите flag.Parse(), +чтобы выполнить парсинг командной строки.

+ +
+ +
    flag.Parse()
+
+ +
+

Здесь мы просто выведем результат парсинга и все +введеные аргументы. Обратите внимание, что нам +нужно разыменовать указатели, например, с +помощью *wordPtr, чтобы получить фактические +значения.

+ +
+ +
    fmt.Println("word:", *wordPtr)
+    fmt.Println("numb:", *numbPtr)
+    fmt.Println("fork:", *boolPtr)
+    fmt.Println("svar:", svar)
+    fmt.Println("tail:", flag.Args())
+}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Чтобы поэкспериментировать с программой флагов командной +строки, лучше сначала скомпилировать ее, а затем +напрямую запустить полученный бинарный файл.

+ +
+ +
$ go build command-line-flags.go
+
+ +
+

Попробуйте встроенную программу, сначала задав ей +значения для всех флагов.

+ +
+ +
$ ./command-line-flags -word=opt -numb=7 -fork -svar=flag
+word: opt
+numb: 7
+fork: true
+svar: flag
+tail: []
+
+ +
+

Обратите внимание, что если вы опустите флаги, они +автоматически примут свои значения по умолчанию.

+ +
+ +
$ ./command-line-flags -word=opt
+word: opt
+numb: 42
+fork: false
+svar: bar
+tail: []
+
+ +
+

Конечные позиционные аргументы могут быть +предоставлены после любых флагов.

+ +
+ +
$ ./command-line-flags -word=opt a1 a2 a3
+word: opt
+...
+tail: [a1 a2 a3]
+
+ +
+

Обратите внимание, что пакет flag требует, чтобы все +флаги отображались перед позиционными аргументами +(в противном случае флаги будут интерпретироваться +как позиционные аргументы).

+ +
+ +
$ ./command-line-flags -word=opt a1 a2 a3 -numb=7
+word: opt
+numb: 42
+fork: false
+svar: bar
+tail: [a1 a2 a3 -numb=7]
+
+ +
+

Используйте флаги -h или --help, чтобы получить +автоматически сгенерированный текст справки для +программы командной строки.

+ +
+ +
$ ./command-line-flags -h
+Usage of ./command-line-flags:
+  -fork=false: a bool
+  -numb=42: an int
+  -svar="bar": a string var
+  -word="foo": a string
+
+ +
+

Если вы укажете флаг, который не был указан для пакета +флагов, программа напечатает сообщение об ошибке +и снова покажет текст справки.

+ +
+ +
$ ./command-line-flags -wat
+flag provided but not defined: -wat
+Usage of ./command-line-flags:
+...
+
+ +
+

Следующий пример: Подкоманды командной строки (Command-Line Subcommands). @@ -34,7 +315,7 @@

diff --git a/public/command-line-subcommands b/public/command-line-subcommands index f90fce7..e618477 100644 --- a/public/command-line-subcommands +++ b/public/command-line-subcommands @@ -23,6 +23,235 @@

Go в примерах: Подкоманды командной строки (Command-Line Subcommands)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Некоторые инструменты командной строки, такие как go +или git, имеют много подкоманд, каждая со своим +собственным набором флагов. Например, go build и +go get - это две разные подкоманды инструмента go. +Пакет flag позволяет нам легко определять простые +подкоманды, которые имеют свои собственные флаги.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "flag"
+    "fmt"
+    "os"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Мы объявляем подкоманду, используя функцию NewFlagSet, +и приступаем к определению новых флагов, специфичных +для этой подкоманды.

+ +
+ +
    fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
+    fooEnable := fooCmd.Bool("enable", false, "enable")
+    fooName := fooCmd.String("name", "", "name")
+
+ +
+

Для другой подкоманды мы можем определить другие +флаги.

+ +
+ +
    barCmd := flag.NewFlagSet("bar", flag.ExitOnError)
+    barLevel := barCmd.Int("level", 0, "level")
+
+ +
+

Подкоманда ожидается в качестве первого аргумента +программы.

+ +
+ +
    if len(os.Args) < 2 {
+        fmt.Println("expected 'foo' or 'bar' subcommands")
+        os.Exit(1)
+    }
+
+ +
+

Проверяем, какая подкоманда вызвана.

+ +
+ +
    switch os.Args[1] {
+
+ +
+

Для каждой подкоманды мы анализируем ее собственные +флаги и имеем доступ к аргументам.

+ +
+ +
    case "foo":
+        fooCmd.Parse(os.Args[2:])
+        fmt.Println("subcommand 'foo'")
+        fmt.Println("  enable:", *fooEnable)
+        fmt.Println("  name:", *fooName)
+        fmt.Println("  tail:", fooCmd.Args())
+    case "bar":
+        barCmd.Parse(os.Args[2:])
+        fmt.Println("subcommand 'bar'")
+        fmt.Println("  level:", *barLevel)
+        fmt.Println("  tail:", barCmd.Args())
+    default:
+        fmt.Println("expected 'foo' or 'bar' subcommands")
+        os.Exit(1)
+    }
+}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
$ go build command-line-subcommands.go
+
+ +
+

Первый вызов подкоманды foo.

+ +
+ +
$ ./command-line-subcommands foo -enable -name=joe a1 a2
+subcommand 'foo'
+  enable: true
+  name: joe
+  tail: [a1 a2]
+
+ +
+

Теперь пробуем bar.

+ +
+ +
$ ./command-line-subcommands bar -level 8 a1
+subcommand 'bar'
+  level: 8
+  tail: [a1]
+
+ +
+

Но bar не может принимать флаги определенные для foo.

+ +
+ +
$ ./command-line-subcommands bar -enable a1
+flag provided but not defined: -enable
+Usage of bar:
+  -level int
+        level
+
+ +
+

Далее мы рассмотрим переменные окружения, еще один +распространенный способ параметризации программ.

+ +
+ + +
+

Следующий пример: Переменные среды (Environment Variables). @@ -34,7 +263,7 @@

diff --git a/public/constants b/public/constants index bc51abe..6e28a5e 100644 --- a/public/constants +++ b/public/constants @@ -23,6 +23,155 @@

Go в примерах: Константы (Constants)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

В Go константы могут принимать значения следующих типов: +строки, числа и логические значения

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "math"
+)
+
+ +
+

Для объявления константы используется ключевое слово const.

+ +
+ +
const s string = "constant"
+
+ +
+ + + +
func main() {
+    fmt.Println(s)
+
+ +
+

Оператор const может использоваться везде, где может +быть использован оператор var.

+ +
+ +
    const n = 500000000
+
+ +
+

Постоянные выражения выполняют арифметику с +произвольной точностью.

+ +
+ +
    const d = 3e20 / n
+    fmt.Println(d)
+
+ +
+

Числовая константа не имеет типа до тех пор, +пока ей не присвоен, например, при явном преобразовании.

+ +
+ +
    fmt.Println(int64(d))
+
+ +
+

Число может использоваться в контексте, который требует +его, например, присваивание переменной или вызов +функции. Например, здесь math.Sin ожидает +float64.

+ +
+ +
    fmt.Println(math.Sin(n))
+}
+
+ +
+ + + + + + + + +
+ + + +
$ go run constant.go 
+constant
+6e+11
+600000000000
+-0.28470407323754404
+
+ +
+

Следующий пример: Цикл For. @@ -34,7 +183,7 @@

diff --git a/public/directories b/public/directories index 2dd5c88..a987599 100644 --- a/public/directories +++ b/public/directories @@ -23,6 +23,325 @@

Go в примерах: Директории (Directories)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Go имеет несколько полезных функций для работы +с каталогами в файловой системе.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "io/ioutil"
+    "os"
+    "path/filepath"
+)
+
+ +
+ + + +
func check(e error) {
+    if e != nil {
+        panic(e)
+    }
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Создадим новую суб-директорию в текущей рабочей +папке.

+ +
+ +
    err := os.Mkdir("subdir", 0755)
+    check(err)
+
+ +
+

Когда мы создаем временную директорию, хорошим +тоном является удалить ее через defer. +os.RemoveAll удалит директорию и все, что в ней +находится (по аналогии с rm -rf).

+ +
+ +
    defer os.RemoveAll("subdir")
+
+ +
+

Функция-помощник для создания нового пустого файла.

+ +
+ +
    createEmptyFile := func(name string) {
+        d := []byte("")
+        check(ioutil.WriteFile(name, d, 0644))
+    }
+
+ +
+ + + +
    createEmptyFile("subdir/file1")
+
+ +
+

Мы можем создать иерархию из директорий, включая все +родительские, с помощью MkdirAll. Это является аналогом +команды mkdir -p.

+ +
+ +
    err = os.MkdirAll("subdir/parent/child", 0755)
+    check(err)
+
+ +
+ + + +
    createEmptyFile("subdir/parent/file2")
+    createEmptyFile("subdir/parent/file3")
+    createEmptyFile("subdir/parent/child/file4")
+
+ +
+

ReadDir перечисляет содержимое каталогов, +возвращая срез объектов os.FileInfo.

+ +
+ +
    c, err := ioutil.ReadDir("subdir/parent")
+    check(err)
+
+ +
+ + + +
    fmt.Println("Listing subdir/parent")
+    for _, entry := range c {
+        fmt.Println(" ", entry.Name(), entry.IsDir())
+    }
+
+ +
+

Chdir позволяет изменить текущую рабочую +директорию, по аналогии с cd.

+ +
+ +
    err = os.Chdir("subdir/parent/child")
+    check(err)
+
+ +
+

Теперь мы увидим содержимое директории +subdir/parent/child, когда запросим листинг +текущей директории.

+ +
+ +
    c, err = ioutil.ReadDir(".")
+    check(err)
+
+ +
+ + + +
    fmt.Println("Listing subdir/parent/child")
+    for _, entry := range c {
+        fmt.Println(" ", entry.Name(), entry.IsDir())
+    }
+
+ +
+

Вернемся в начало

+ +
+ +
    err = os.Chdir("../../..")
+    check(err)
+
+ +
+

Мы также можем рекурсивно обойти каталог, включая +все его подкаталоги. Walk принимает функцию обратного +вызова для обработки каждого файла или каталога, +которые посетили.

+ +
+ +
    fmt.Println("Visiting subdir")
+    err = filepath.Walk("subdir", visit)
+}
+
+ +
+

visit вызывается для каждого найденного элемента в filepath.Walk.

+ +
+ +
func visit(p string, info os.FileInfo, err error) error {
+    if err != nil {
+        return err
+    }
+    fmt.Println(" ", p, info.IsDir())
+    return nil
+}
+
+ +
+ + + + + + + + +
+ + + +
$ go run directories.go
+Listing subdir/parent
+  child true
+  file2 false
+  file3 false
+Listing subdir/parent/child
+  file4 false
+Visiting subdir
+  subdir true
+  subdir/file1 false
+  subdir/parent true
+  subdir/parent/child true
+  subdir/parent/child/file4 false
+  subdir/parent/file2 false
+  subdir/parent/file3 false
+
+ +
+

Следующий пример: Временные файлы и директории (Temporary Files and Directories). @@ -34,7 +353,7 @@

diff --git a/public/environment-variables b/public/environment-variables index c08bf0e..2ff35bd 100644 --- a/public/environment-variables +++ b/public/environment-variables @@ -14,7 +14,7 @@ if (e.key == "ArrowRight") { - window.location.href = 'http'; + window.location.href = 'http-clients'; } } @@ -23,9 +23,162 @@

Go в примерах: Переменные среды (Environment Variables)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Переменные среды (или переменные окружения) +- это универсальный механизм передачи информации о конфигурации +в программы Unix. Давайте +посмотрим, как устанавливать, получать и перечислять переменные +среды.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "os"
+    "strings"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Чтобы установить пару ключ/значение, используйте +os.Setenv. Чтобы получить значение для ключа, +используйте os.Getenv. Это вернет пустую строку, +если ключ не присутствует в среде.

+ +
+ +
    os.Setenv("FOO", "1")
+    fmt.Println("FOO:", os.Getenv("FOO"))
+    fmt.Println("BAR:", os.Getenv("BAR"))
+
+ +
+

Используйте os.Environ для вывода списка всех пар +ключ/значение в среде. Это возвращает спез строк в +формате KEY=value. Вы можете использовать strings.Split, +чтобы получить ключ и значение. Здесь мы печатаем +все ключи.

+ +
+ +
    fmt.Println()
+    for _, e := range os.Environ() {
+        pair := strings.Split(e, "=")
+        fmt.Println(pair[0])
+    }
+}
+
+ +
+ + + + + + + + + + + + + + + + + + +
+

Запуск программы показывает, что мы выбираем значение +для FOO, которое мы установили в программе, но BAR +пуст.

+ +
+ +
$ go run environment-variables.go
+FOO: 1
+BAR: 
+
+ +
+

Список ключей в среде будет зависеть от вашей системы.

+ +
+ +
TERM_PROGRAM
+PATH
+SHELL
+...
+
+ +
+

Если мы сначала установим BAR в среде, запущенная +программа использует это значение.

+ +
+ +
$ BAR=2 go run environment-variables.go
+FOO: 1
+BAR: 2
+...
+
+ +
+

- Следующий пример: HTTP клиенты (HTTP Clients). + Следующий пример: HTTP клиенты (HTTP Clients).

diff --git a/public/errors b/public/errors index d276119..e4a84e5 100644 --- a/public/errors +++ b/public/errors @@ -23,6 +23,270 @@

Go в примерах: Ошибки (Errors)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

В Go принято сообщать об ошибках через явное, отдельное +возвращаемое значение. Это контрастирует с исключениями, +используемыми в таких языках, как Java и Ruby, и +перегруженным одиночным значением результата/ошибки, +иногда используемым в подходе C. Go, позволяет легко +увидеть, какие функции возвращают ошибки, и обрабатывать +их, используя те же языковые конструкции, которые +используются для любых других задач без ошибок.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "errors"
+    "fmt"
+)
+
+ +
+

По соглашению, ошибки - это последнее возвращаемое +значение с типом error в стандартной реализации.

+ +
+ +
func f1(arg int) (int, error) {
+    if arg == 42 {
+
+ +
+

errors.New создает стандартную ошибку с +указаннным сообщением

+ +
+ +
        return -1, errors.New("can't work with 42")
+
+ +
+ + + +
    }
+
+ +
+

Значение nil в поле ошибки, говорит о том, что +ошибок нет.

+ +
+ +
    return arg + 3, nil
+}
+
+ +
+

Можно использовать пользовательские типы в качестве +ошибок, применяя к ним метод Error(). Вот вариант +в примере выше, который использует пользовательский +тип для явного представления ошибки аргумента.

+ +
+ +
type argError struct {
+    arg  int
+    prob string
+}
+
+ +
+ + + +
func (e *argError) Error() string {
+    return fmt.Sprintf("%d - %s", e.arg, e.prob)
+}
+
+ +
+ + + +
func f2(arg int) (int, error) {
+    if arg == 42 {
+
+ +
+

В этом случае мы используем синтаксис &argError +для создания новой структуры, предоставляя +значения для двух полей arg и prob.

+ +
+ +
        return -1, &argError{arg, "can't work with it"}
+    }
+    return arg + 3, nil
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Два цикла ниже тестируют каждую из наших функций, +возвращающих ошибки. Обратите внимание, что +использование встроенной проверки ошибок в строке +if является обычный явлением в Go.

+ +
+ +
    for _, i := range []int{7, 42} {
+        if r, e := f1(i); e != nil {
+            fmt.Println("f1 failed:", e)
+        } else {
+            fmt.Println("f1 worked:", r)
+        }
+    }
+    for _, i := range []int{7, 42} {
+        if r, e := f2(i); e != nil {
+            fmt.Println("f2 failed:", e)
+        } else {
+            fmt.Println("f2 worked:", r)
+        }
+    }
+
+ +
+

Если вы хотите использовать данные в пользовательской +ошибке, вам нужно получить ошибку как экземпляр +пользовательского типа через утверждение типа.

+ +
+ +
    _, e := f2(42)
+    if ae, ok := e.(*argError); ok {
+        fmt.Println(ae.arg)
+        fmt.Println(ae.prob)
+    }
+}
+
+ +
+ + + + + + + + + + + + + +
+ + + +
$ go run errors.go
+f1 worked: 10
+f1 failed: can't work with 42
+f2 worked: 10
+f2 failed: 42 - can't work with it
+42
+can't work with it
+
+ +
+

Посмотрите эту статью +в блоге Go для более подробной информации об ошибках.

+ +
+ + +
+

Следующий пример: Горутины (Goroutines). @@ -34,7 +298,7 @@

diff --git a/public/execing-processes b/public/execing-processes index e0cc10d..18b0fc5 100644 --- a/public/execing-processes +++ b/public/execing-processes @@ -23,6 +23,174 @@

Go в примерах: Исполняющие процессы (Exec'ing Processes)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

В предыдущем примере мы рассмотрели порождение внешних +процессов. Мы делаем это, +когда нам нужен внешний процесс, доступный для +запущенного процесса Go. Иногда мы просто хотим +полностью заменить текущий процесс Go другим +(возможно, не Go). Для этого мы будем использовать +реализацию Go-имплементацию классической функции +exec.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "os"
+    "os/exec"
+    "syscall"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Для нашего примера мы выполним ls. Go требует +абсолютного пути к двоичному файлу, который мы +хотим выполнить, поэтому мы используем exec.LookPath, +чтобы найти его (вероятно, /bin/ls).

+ +
+ +
    binary, lookErr := exec.LookPath("ls")
+    if lookErr != nil {
+        panic(lookErr)
+    }
+
+ +
+

Exec требует аргументы в форме среза +(в сочетании с одной большой строкой). Мы используем в ls +несколько общих аргументов. Обратите внимание, что +первым аргументом должно быть имя программы.

+ +
+ +
    args := []string{"ls", "-a", "-l", "-h"}
+
+ +
+

Exec также нужен набор переменных среды +для использования. Здесь мы просто предоставляем +нашу текущую среду.

+ +
+ +
    env := os.Environ()
+
+ +
+

Вот фактический вызов syscall.Exec. Если этот вызов +будет успешным, выполнение нашего процесса на этом +закончится и будет заменено процессом /bin/ls -a -l -h. +В случае ошибки мы получим возвращаемое значение.

+ +
+ +
    execErr := syscall.Exec(binary, args, env)
+    if execErr != nil {
+        panic(execErr)
+    }
+}
+
+ +
+ + + + + + + + + + + + + +
+

When we run our program it is replaced by ls.

+ +
+ +
$ go run execing-processes.go
+total 16
+drwxr-xr-x  4 mark 136B Oct 3 16:29 .
+drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
+-rw-r--r--  1 mark 1.3K Oct 3 16:28 execing-processes.go
+
+ +
+

Обратите внимание, что Go не предлагает классическую +Unix функцию форка. Обычно это не проблема, +так как запуск горутин, порождение процессов и +выполнение процессов покрывают большинство случаев +использования форка.

+ +
+ + +
+

Следующий пример: Сигналы (Signals). @@ -34,7 +202,7 @@

diff --git a/public/exit b/public/exit index 50c7fbe..3611088 100644 --- a/public/exit +++ b/public/exit @@ -19,6 +19,153 @@

Go в примерах: Выход (Exit)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Используйте os.Exit для немедленного выхода с +полученныем статусом.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "os"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

defer не будет запускаться при использовании +os.Exit, поэтому этот fmt.Println никогда не +будет вызываться.

+ +
+ +
    defer fmt.Println("!")
+
+ +
+

Выход со статусом 3.

+ +
+ +
    os.Exit(3)
+}
+
+ +
+

Обратите внимание, что в отличие, например, от C, +Go не использует целочисленное возвращаемое значение +из main, чтобы указать состояние выхода. Если +вы хотите выйти с ненулевым статусом, вы должны +использовать os.Exit.

+ +
+ + +
+ + + + + + + + + + + + + + + + + + +
+

Если вы запустите exit.go с помощью go run, +выход будет выбран go и напечатан.

+ +
+ +
$ go run exit.go
+exit status 3
+
+ +
+

Создавая и выполняя двоичный файл, вы можете +увидеть статус в терминале.

+ +
+ +
$ go build exit.go
+$ ./exit
+$ echo $?
+3
+
+ +
+

Обратите внимание, что ! из нашей программы +никогда не был напечатан.

+ +
+ + +
+
diff --git a/public/file-paths b/public/file-paths index b026eb7..bac50bb 100644 --- a/public/file-paths +++ b/public/file-paths @@ -23,6 +23,222 @@

Go в примерах: Пути к файлам (File Paths)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Пакет filepath предоставляет функции для разбора и +создания путей к файлам способом, который переносим +между операционными системами; например, dir/file в +Linux против dir\file в Windows.

+ +
+ +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "path/filepath"
+    "strings"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Join должен использоваться для создания путей в +переносимом виде. Он принимает любое количество +аргументов и строит из них иерархический путь.

+ +
+ +
    p := filepath.Join("dir1", "dir2", "filename")
+    fmt.Println("p:", p)
+
+ +
+

Вы должны всегда использовать Join вместо ручного +объединения с помощью слешей / или \. В дополнение +к обеспечению переносимости, Join также нормализует +пути, удаляя лишние разделители.

+ +
+ +
    fmt.Println(filepath.Join("dir1//", "filename"))
+    fmt.Println(filepath.Join("dir1/../dir1", "filename"))
+
+ +
+

Dir и Base могут использоваться для разделения +пути к каталогу и файлу. В качестве альтернативы, +Split вернет оба в одном вызове.

+ +
+ +
    fmt.Println("Dir(p):", filepath.Dir(p))
+    fmt.Println("Base(p):", filepath.Base(p))
+
+ +
+

Можно проверить является ли путь абсолютным.

+ +
+ +
    fmt.Println(filepath.IsAbs("dir/file"))
+    fmt.Println(filepath.IsAbs("/dir/file"))
+
+ +
+ + + +
    filename := "config.json"
+
+ +
+

Некоторые имена файлов имеют расширения, следующие +за точкой. Мы можем получить расширение из таких +имен с помощью Ext.

+ +
+ +
    ext := filepath.Ext(filename)
+    fmt.Println(ext)
+
+ +
+

Чтобы найти имя файла с удаленным расширением, +используйте strings.TrimSuffix.

+ +
+ +
    fmt.Println(strings.TrimSuffix(filename, ext))
+
+ +
+

Rel находит относительный путь между двумя путями +base и target. Возвращает ошибку, если target +не может быть получен из base.

+ +
+ +
    rel, err := filepath.Rel("a/b", "a/b/t/file")
+    if err != nil {
+        panic(err)
+    }
+    fmt.Println(rel)
+
+ +
+ + + +
    rel, err = filepath.Rel("a/b", "a/c/t/file")
+    if err != nil {
+        panic(err)
+    }
+    fmt.Println(rel)
+}
+
+ +
+ + + + + + + + +
+ + + +
$ go run file-paths.go
+p: dir1/dir2/filename
+dir1/filename
+dir1/filename
+Dir(p): dir1/dir2
+Base(p): filename
+false
+true
+.json
+config
+t/file
+../c/t/file
+
+ +
+

Следующий пример: Директории (Directories). @@ -34,7 +250,7 @@

diff --git a/public/for b/public/for index de2ea5f..975d088 100644 --- a/public/for +++ b/public/for @@ -23,6 +23,167 @@

Go в примерах: Цикл For

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

for - это единственный цикл доступный в Go. +Три стандартных примера использования for

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+ + + +
func main() {
+
+ +
+

Стандартный тип с единственным условием

+ +
+ +
    i := 1
+    for i <= 3 {
+        fmt.Println(i)
+        i = i + 1
+    }
+
+ +
+

Классическая инициализация/условие/выражение после for

+ +
+ +
    for j := 7; j <= 9; j++ {
+        fmt.Println(j)
+    }
+
+ +
+

for без условия будет выполняться бесконечно +пока не выполнится break (выход из цикла) или +return, который завершит функцию с циклом

+ +
+ +
    for {
+        fmt.Println("loop")
+        break
+    }
+
+ +
+

Так же Вы можете использовать continue для +немедленного перехода к следующей итерации цикла

+ +
+ +
    for n := 0; n <= 5; n++ {
+        if n%2 == 0 {
+            continue
+        }
+        fmt.Println(n)
+    }
+}
+
+ +
+ + + + + + + + + + + + + +
+ + + +
$ go run for.go
+1
+2
+3
+7
+8
+9
+loop
+1
+3
+5
+
+ +
+

We’ll see some other for forms later when we look at +range statements, channels, and other data +structures.

+ +
+ + +
+

Следующий пример: If/Else. @@ -34,7 +195,7 @@

diff --git a/public/functions b/public/functions index ef4f0db..ca5cf19 100644 --- a/public/functions +++ b/public/functions @@ -23,6 +23,166 @@

Go в примерах: Функции (Functions)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Функции это сердце языка Go. Мы посмотрим +использование функций на нескольих примерах.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import "fmt"
+
+ +
+

Эта функция принимает в качестве аргументов +два целых числа и возвращает их сумму, так +же с типом целое число.

+ +
+ +
func plus(a int, b int) int {
+
+ +
+

Go требует явного указания типа возвращаемого +значение, то есть он не будет автоматически +возвращать значение последнего выражения.

+ +
+ +
    return a + b
+}
+
+ +
+

Если функция принимает несколько аргументов с +одинаковым типом, то вы можете перечислить аргументы +через запятую и указать тип один раз.

+ +
+ +
func plusPlus(a, b, c int) int {
+    return a + b + c
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Вызов функции осуществялется через запись +функция(аргументы).

+ +
+ +
    res := plus(1, 2)
+    fmt.Println("1+2 =", res)
+
+ +
+ + + +
    res = plusPlus(1, 2, 3)
+    fmt.Println("1+2+3 =", res)
+}
+
+ +
+ + + + + + + + + + + + + +
+ + + +
$ go run functions.go 
+1+2 = 3
+1+2+3 = 6
+
+ +
+

Есть несколько других особенностей для функций в Go. +Одной из них является возможнсоть возврата нескольких +значений, которые мы рассмотрим далее.

+ +
+ + +
+

Следующий пример: Функции с множественным возвратом (Multiple Return Values). @@ -34,7 +194,7 @@

diff --git a/public/goroutines b/public/goroutines index dfcbc2d..0679213 100644 --- a/public/goroutines +++ b/public/goroutines @@ -23,6 +23,179 @@

Go в примерах: Горутины (Goroutines)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Горутины - это легковесный тред.

+ +
+ + +
+ + + +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "time"
+)
+
+ +
+ + + +
func f(from string) {
+    for i := 0; i < 3; i++ {
+        fmt.Println(from, ":", i)
+    }
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Предположим, у нас есть вызов функции f(s). Вот +как мы бы вызвали её обычным способом, запустив +синхронно.

+ +
+ +
    f("direct")
+
+ +
+

Чтобы вызвать эту функцию в горутине, используйте +go f(s). Эта новая горутина будет выполняться +одновременно с вызывающей фукнцией.

+ +
+ +
    go f("goroutine")
+
+ +
+

Вы так же можете вызывать анонимные функции в +горутнах.

+ +
+ +
    go func(msg string) {
+        fmt.Println(msg)
+    }("going")
+
+ +
+

Наши две функции теперь выполняются асинхронно в +отдельных горутинах. Дождитесь их окончания (для +более надежного подхода используйте WaitGroup).

+ +
+ +
    time.Sleep(time.Second)
+    fmt.Println("done")
+}
+
+ +
+ + + + + + + + + + + + + +
+

Когда мы запускаем эту программу, мы видим сначала +вывод блокирующего вызова, а затем чередующийся вывод +двух горутин. Это чередование отражает выполнение +горутин, одновременно выполняемых средой выполнения Go.

+ +
+ +
$ go run goroutines.go
+direct : 0
+direct : 1
+direct : 2
+goroutine : 0
+going
+goroutine : 1
+goroutine : 2
+done
+
+ +
+

Далее мы рассмотрим дополнение к горутинам в +параллельных программах Go: каналы.

+ +
+ + +
+

Следующий пример: Каналы (Channels). @@ -34,7 +207,7 @@

diff --git a/public/http b/public/http deleted file mode 100644 index 1ddb1a7..0000000 --- a/public/http +++ /dev/null @@ -1,41 +0,0 @@ - - - - - Go в примерах: HTTP серверы (HTTP Servers) - - - - -
-

Go в примерах: HTTP серверы (HTTP Servers)

- - -

- Следующий пример: Порождающие процессы (Spawning Processes). -

- - -
- - - - diff --git a/public/http-clients b/public/http-clients new file mode 100644 index 0000000..ebff089 --- /dev/null +++ b/public/http-clients @@ -0,0 +1,175 @@ + + + + + Go в примерах: HTTP клиенты (HTTP Clients) + + + + +
+

Go в примерах: HTTP клиенты (HTTP Clients)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Стандартная библиотека Go поставляется с отличной +поддержкой клиентов и серверов HTTP в пакете net/http. +В этом примере мы будем использовать его для +простых HTTP-запросов.

+ +
+ +
package main
+
+ +
+ + + +
import (
+    "bufio"
+    "fmt"
+    "net/http"
+)
+
+ +
+ + + +
func main() {
+
+ +
+

Отправьте HTTP-запрос GET на сервер. http.Get - это +удобный способ создания объекта http.Client и вызова +его метода Get; он использует объект http.DefaultClient, +который имеет полезные настройки по умолчанию.

+ +
+ +
    resp, err := http.Get("http://gobyexample.com")
+    if err != nil {
+        panic(err)
+    }
+    defer resp.Body.Close()
+
+ +
+

Выведем статус http-ответа.

+ +
+ +
    fmt.Println("Response status:", resp.Status)
+
+ +
+

Выведем первые 5 строк тела ответа.

+ +
+ +
    scanner := bufio.NewScanner(resp.Body)
+    for i := 0; scanner.Scan() && i < 5; i++ {
+        fmt.Println(scanner.Text())
+    }
+
+ +
+ + + +
    if err := scanner.Err(); err != nil {
+        panic(err)
+    }
+}
+
+ +
+ + + + + + + + +
+ + + +
$ go run http-clients.go
+Response status: 200 OK
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Go by Example</title>
+
+ +
+ + +

+ Следующий пример: HTTP серверы (HTTP Servers). +

+ + +
+ + + + diff --git a/public/http-servers b/public/http-servers new file mode 100644 index 0000000..4af4f3a --- /dev/null +++ b/public/http-servers @@ -0,0 +1,218 @@ + + + + + Go в примерах: HTTP серверы (HTTP Servers) + + + + +
+

Go в примерах: HTTP серверы (HTTP Servers)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Написание базового HTTP сервера очень легкореализуемо +с пакетом net/http.

+ +
+ +
package main
+
+ +
+ + + +
import (
+    "fmt"
+    "net/http"
+)
+
+ +
+

Фундаментальная концепция серверов net/http - это +обработчики. Обработчик - это объект, реализующий +интерфейс http.Handler. Распространенным способом +написания обработчика является использование адаптера +http.HandlerFunc для функций с соответствующей +подписью.

+ +
+ +
func hello(w http.ResponseWriter, req *http.Request) {
+
+ +
+

Функции, выполняющие функции обработчиков, принимают +в качестве аргументов http.ResponseWriter и +http.Request. Response writer используется для +наполнения HTTP-ответа. Здесь наш простой ответ +“hello\n”.

+ +
+ +
    fmt.Fprintf(w, "hello\n")
+}
+
+ +
+ + + +
func headers(w http.ResponseWriter, req *http.Request) {
+
+ +
+

Этот обработчик делает что-то более сложное, +читая все заголовки HTTP-запроса и вставляя их в +тело ответа.

+ +
+ +
    for name, headers := range req.Header {
+        for _, h := range headers {
+            fmt.Fprintf(w, "%v: %v\n", name, h)
+        }
+    }
+}
+
+ +
+ + + +
func main() {
+
+ +
+

Мы регистрируем наши обработчики на сервере, +используя удобную функцию http.HandleFunc. Она +устанавливает маршрут по умолчанию в пакете +net/http и принимает функцию в качестве аргумента.

+ +
+ +
    http.HandleFunc("/hello", hello)
+    http.HandleFunc("/headers", headers)
+
+ +
+

Наконец, мы вызываем ListenAndServe с портом и +обработчиком. nil говорит использовать только что +настроенный маршрутизатор по умолчанию.

+ +
+ +
    http.ListenAndServe(":8090", nil)
+}
+
+ +
+ + + + + + + + + + + + + +
+

Запускаем сервер в фоне.

+ +
+ +
$ go run http-servers.go &
+
+ +
+

Делаем запрос по адресу /hello.

+ +
+ +
$ curl localhost:8090/hello
+hello
+
+ +
+ + +

+ Следующий пример: Порождающие процессы (Spawning Processes). +

+ + +
+ + + + diff --git a/public/index.html b/public/index.html index 9a280e9..9bce2be 100644 --- a/public/index.html +++ b/public/index.html @@ -25,151 +25,151 @@
  • Hello World
  • -
  • Типы данных (Values)
  • +
  • Типы данных (Values)
  • -
  • Переменные (Variables)
  • +
  • Переменные (Variables)
  • -
  • Константы (Constants)
  • +
  • Константы (Constants)
  • -
  • Цикл For
  • +
  • Цикл For
  • If/Else
  • Switch
  • -
  • Массивы (Arrays)
  • +
  • Массивы (Arrays)
  • -
  • Срезы (Slices)
  • +
  • Срезы (Slices)
  • -
  • Карты (Maps)
  • +
  • Карты (Maps)
  • -
  • Ряд (Range)
  • +
  • Ряд (Range)
  • -
  • Функции (Functions)
  • +
  • Функции (Functions)
  • -
  • Функции с множественным возвратом (Multiple Return Values)
  • +
  • Функции с множественным возвратом (Multiple Return Values)
  • -
  • Функции с переменным числом аргументов (Variadic Functions)
  • +
  • Функции с переменным числом аргументов (Variadic Functions)
  • -
  • Замыкания (Closures)
  • +
  • Замыкания (Closures)
  • -
  • Рекурсия (Recursion)
  • +
  • Рекурсия (Recursion)
  • -
  • Указатели (Pointers)
  • +
  • Указатели (Pointers)
  • -
  • Структуры (Structs)
  • +
  • Структуры (Structs)
  • -
  • Методы (Methods)
  • +
  • Методы (Methods)
  • -
  • Интерфейсы (Interfaces)
  • +
  • Интерфейсы (Interfaces)
  • -
  • Ошибки (Errors)
  • +
  • Ошибки (Errors)
  • -
  • Горутины (Goroutines)
  • +
  • Горутины (Goroutines)
  • -
  • Каналы (Channels)
  • +
  • Каналы (Channels)
  • -
  • Буферизированный канал (Channel Buffering)
  • +
  • Буферизированный канал (Channel Buffering)
  • -
  • Синхронизация канала (Channel Synchronization)
  • +
  • Синхронизация канала (Channel Synchronization)
  • -
  • Направления канала (Channel Directions)
  • +
  • Направления канала (Channel Directions)
  • Select
  • -
  • Тайм-ауты (Timeouts)
  • +
  • Тайм-ауты (Timeouts)
  • -
  • Неблокируемые операции в каналах (Non-Blocking Channel Operations)
  • +
  • Неблокируемые операции в каналах (Non-Blocking Channel Operations)
  • -
  • Закрытие каналов (Closing Channels)
  • +
  • Закрытие каналов (Closing Channels)
  • -
  • Перебор значений из каналов (Range over Channels)
  • +
  • Перебор значений из каналов (Range over Channels)
  • -
  • Таймеры (Timers)
  • +
  • Таймеры (Timers)
  • -
  • Тикеры (повторения) (Tickers)
  • +
  • Тикеры (повторения) (Tickers)
  • -
  • Пулы воркеров (Worker Pools)
  • +
  • Пулы воркеров (Worker Pools)
  • WaitGroups
  • -
  • Ограничение скорости (Rate Limiting)
  • +
  • Ограничение скорости (Rate Limiting)
  • -
  • Атомарные счетчики (Atomic Counters)
  • +
  • Атомарные счетчики (Atomic Counters)
  • -
  • Мьютексы (Mutexes)
  • +
  • Мьютексы (Mutexes)
  • -
  • Управление состоянием горутин (Stateful Goroutines)
  • +
  • Управление состоянием горутин (Stateful Goroutines)
  • -
  • Сортировка (Sorting)
  • +
  • Сортировка (Sorting)
  • -
  • Сортировка через функции (Sorting by Functions)
  • +
  • Сортировка через функции (Sorting by Functions)
  • Panic
  • Defer
  • -
  • Функции коллекции (Collection Functions)
  • +
  • Функции коллекции (Collection Functions)
  • -
  • Строковые функции (String Functions)
  • +
  • Строковые функции (String Functions)
  • -
  • Форматирование строк (String Formatting)
  • +
  • Форматирование строк (String Formatting)
  • -
  • Регулярные выражения (Regular Expressions)
  • +
  • Регулярные выражения (Regular Expressions)
  • JSON
  • XML
  • -
  • Время (Time)
  • +
  • Время (Time)
  • Epoch
  • -
  • Форматирование времени (Time Formatting / Parsing)
  • +
  • Форматирование времени (Time Formatting / Parsing)
  • -
  • Случайные числа (Random Numbers)
  • +
  • Случайные числа (Random Numbers)
  • -
  • Парсинг чисел (Number Parsing)
  • +
  • Парсинг чисел (Number Parsing)
  • -
  • Парсинг URL (URL Parsing)
  • +
  • Парсинг URL (URL Parsing)
  • -
  • Хеш SHA1 (SHA1 Hashes)
  • +
  • Хеш SHA1 (SHA1 Hashes)
  • -
  • Кодирование Base64 (Base64 Encoding)
  • +
  • Кодирование Base64 (Base64 Encoding)
  • -
  • Чтение файлов (Reading Files)
  • +
  • Чтение файлов (Reading Files)
  • -
  • Запись файлов (Writing Files)
  • +
  • Запись файлов (Writing Files)
  • -
  • Строковые фильтры (Line Filters)
  • +
  • Строковые фильтры (Line Filters)
  • -
  • Пути к файлам (File Paths)
  • +
  • Пути к файлам (File Paths)
  • -
  • Директории (Directories)
  • +
  • Директории (Directories)
  • -
  • Временные файлы и директории (Temporary Files and Directories)
  • +
  • Временные файлы и директории (Temporary Files and Directories)
  • -
  • Тестирование (Testing)
  • +
  • Тестирование (Testing)
  • -
  • Аргументы командной строки (Command-Line Arguments)
  • +
  • Аргументы командной строки (Command-Line Arguments)
  • -
  • Флаги командной строки (Command-Line Flags)
  • +
  • Флаги командной строки (Command-Line Flags)
  • -
  • Подкоманды командной строки (Command-Line Subcommands)
  • +
  • Подкоманды командной строки (Command-Line Subcommands)
  • -
  • Переменные среды (Environment Variables)
  • +
  • Переменные среды (Environment Variables)
  • -
  • HTTP клиенты (HTTP Clients)
  • +
  • HTTP клиенты (HTTP Clients)
  • -
  • HTTP серверы (HTTP Servers)
  • +
  • HTTP серверы (HTTP Servers)
  • -
  • Порождающие процессы (Spawning Processes)
  • +
  • Порождающие процессы (Spawning Processes)
  • -
  • Исполняющие процессы (Exec'ing Processes)
  • +
  • Исполняющие процессы (Exec'ing Processes)
  • -
  • Сигналы (Signals)
  • +
  • Сигналы (Signals)
  • -
  • Выход (Exit)
  • +
  • Выход (Exit)
  • Go в примерах: Интерфейсы (Interfaces)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Интерфейсы это коллекции методов.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "math"
    +)
    +
    + +
    +

    Пример базового интерфейса в Go

    + +
    + +
    type geometry interface {
    +    area() float64
    +    perim() float64
    +}
    +
    + +
    +

    В нашем примере мы будем реализовывать этот интерфейс +для типов rect и circle.

    + +
    + +
    type rect struct {
    +    width, height float64
    +}
    +type circle struct {
    +    radius float64
    +}
    +
    + +
    +

    Чтобы реализовать интерфейс на Go, нам просто нужно +реализовать все методы в интерфейсе. Здесь мы +реализуем интерфейс geometry для rect.

    + +
    + +
    func (r rect) area() float64 {
    +    return r.width * r.height
    +}
    +func (r rect) perim() float64 {
    +    return 2*r.width + 2*r.height
    +}
    +
    + +
    +

    Реализация для circle.

    + +
    + +
    func (c circle) area() float64 {
    +    return math.Pi * c.radius * c.radius
    +}
    +func (c circle) perim() float64 {
    +    return 2 * math.Pi * c.radius
    +}
    +
    + +
    +

    Если переменная реализует интерфейс, то мы можем +вызывать методы, которые находятся в этом интерфейсе. +Функция measure использует это преимущество, для +работы с любой фигурой.

    + +
    + +
    func measure(g geometry) {
    +    fmt.Println(g)
    +    fmt.Println(g.area())
    +    fmt.Println(g.perim())
    +}
    +
    + +
    + + + +
    func main() {
    +    r := rect{width: 3, height: 4}
    +    c := circle{radius: 5}
    +
    + +
    +

    Типы circle и rect структур реализуют +интерфейс geometry, поэтому мы можем использовать +экземпляры этих структур в качестве аргументов для +measure.

    + +
    + +
        measure(r)
    +    measure(c)
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run interfaces.go
    +{3 4}
    +12
    +14
    +{5}
    +78.53981633974483
    +31.41592653589793
    +
    + +
    +

    Чтобы узнать больше об интерфейсах Go, ознакомьтесь с +этой статьей.

    + +
    + + +
    +

    Следующий пример: Ошибки (Errors). @@ -34,7 +235,7 @@

    diff --git a/public/line-filters b/public/line-filters index 92aaa21..41dc487 100644 --- a/public/line-filters +++ b/public/line-filters @@ -23,6 +23,179 @@

    Go в примерах: Строковые фильтры (Line Filters)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Строковый фильтр - это типичный тип программы, +которая читает входные данные в stdin, обрабатывает +их и затем выводит некоторый производный результат в +стандартный вывод. grep и sed - это обычные строковые +фильтры.

    + +
    + + +
    +

    Вот пример строкового фильтра в Go, который записывает +заглавную версию всего входного текста. Вы можете +использовать этот шаблон для написания ваших собственных +фильтров Go.

    + +
    + +
    package main
    +
    + +
    + + + +
    import (
    +    "bufio"
    +    "fmt"
    +    "os"
    +    "strings"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Обертывание небуферизованного os.Stdin буферизованным +сканером дает нам удобный метод сканирования Scan, +который продвигает сканер к следующему токену; который +является следующей строкой в сканере по умолчанию.

    + +
    + +
        scanner := bufio.NewScanner(os.Stdin)
    +
    + +
    +

    Text возвращает текущий токен, из ввода - на +следующую строку.

    + +
    + +
        for scanner.Scan() {
    +
    + +
    + + + +
            ucl := strings.ToUpper(scanner.Text())
    +
    + +
    +

    Печатает в верхнем регистре.

    + +
    + +
            fmt.Println(ucl)
    +    }
    +
    + +
    +

    Проверяем ошибки для Scan. Ожидается конец файла, +и он не сообщается методом Scan как ошибка.

    + +
    + +
        if err := scanner.Err(); err != nil {
    +        fmt.Fprintln(os.Stderr, "error:", err)
    +        os.Exit(1)
    +    }
    +}
    +
    + +
    + + + + + + + + + + + + + +
    +

    Чтобы опробовать наш фильтр строк, сначала создайте +файл с несколькими строчными строчками.

    + +
    + +
    $ echo 'hello'   > /tmp/lines
    +$ echo 'filter' >> /tmp/lines
    +
    + +
    +

    Затем используйте фильтр строк, чтобы получить строчные +буквы.

    + +
    + +
    $ cat /tmp/lines | go run line-filters.go
    +HELLO
    +FILTER
    +
    + +
    +

    Следующий пример: Пути к файлам (File Paths). @@ -34,7 +207,7 @@

    diff --git a/public/maps b/public/maps index 8d69719..7429741 100644 --- a/public/maps +++ b/public/maps @@ -23,6 +23,204 @@

    Go в примерах: Карты (Maps)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Карты - это встроенный ассоциативный тип данных +Go (иногда называемый хешами).

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Для создания пустой карты, используйте make: +make(map[key-type]val-type).

    + +
    + +
        m := make(map[string]int)
    +
    + +
    +

    Вы можете установить пару ключ/значение +используя привычный синтаксис name[key] = val

    + +
    + +
        m["k1"] = 7
    +    m["k2"] = 13
    +
    + +
    +

    Вывод карты на экран с помощью fmt.Println +выведет все пары ключ/значение

    + +
    + +
        fmt.Println("map:", m)
    +
    + +
    +

    Получить значение по ключу name[key].

    + +
    + +
        v1 := m["k1"]
    +    fmt.Println("v1: ", v1)
    +
    + +
    +

    Встроенная функция len возвращает количество +пар ключ/значение для карты.

    + +
    + +
        fmt.Println("len:", len(m))
    +
    + +
    +

    Встроенная функция delete удаляет пару key/value +из карты.

    + +
    + +
        delete(m, "k2")
    +    fmt.Println("map:", m)
    +
    + +
    +

    Необязательное второе возвращаемое значение +из карты сообщает о том, существовал ли ключ в карте. +Это может быть использовано для устранения +неоднозначности между отсутствующими ключами и +ключами с нулевыми значениями, такими как 0 или +“”. Здесь нам не нужно само значение, поэтому +мы проигнорировали его с пустым идентификатором _.

    + +
    + +
        _, prs := m["k2"]
    +    fmt.Println("prs:", prs)
    +
    + +
    +

    Вы можете объявить и наполнить карту в одной +строке с помощью подобного синтаксиса.

    + +
    + +
        n := map[string]int{"foo": 1, "bar": 2}
    +    fmt.Println("map:", n)
    +}
    +
    + +
    + + + + + + + + +
    +

    Обратите внимание, что карты отображаются в виде +map[k:v k:v]при печати с помощью fmt.Println

    + +
    + +
    $ go run maps.go 
    +map: map[k1:7 k2:13]
    +v1:  7
    +len: 2
    +map: map[k1:7]
    +prs: false
    +map: map[bar:2 foo:1]
    +
    + +
    +

    Следующий пример: Ряд (Range). @@ -34,7 +232,7 @@

    diff --git a/public/methods b/public/methods index 268a326..12b866f 100644 --- a/public/methods +++ b/public/methods @@ -23,6 +23,170 @@

    Go в примерах: Методы (Methods)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go поддерживает методы для структур

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    type rect struct {
    +    width, height int
    +}
    +
    + +
    +

    Метод area принимает получателя *rect.

    + +
    + +
    func (r *rect) area() int {
    +    return r.width * r.height
    +}
    +
    + +
    +

    Методы могут принимать как указатели, так и значения. +Вот пример со значением.

    + +
    + +
    func (r rect) perim() int {
    +    return 2*r.width + 2*r.height
    +}
    +
    + +
    + + + +
    func main() {
    +    r := rect{width: 10, height: 5}
    +
    + +
    +

    Вызываем 2 метода, определенные для нашей структуры.

    + +
    + +
        fmt.Println("area: ", r.area())
    +    fmt.Println("perim:", r.perim())
    +
    + +
    +

    Go автоматически обрабатывает преобразование между +значениями и указателями при вызове методов. +Возможно, вы захотите использовать указатель в +качестве получателя, чтобы избежать копирования при вызове +метода или позволить методу изменять структуру +получателя.

    + +
    + +
        rp := &r
    +    fmt.Println("area: ", rp.area())
    +    fmt.Println("perim:", rp.perim())
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run methods.go 
    +area:  50
    +perim: 30
    +area:  50
    +perim: 30
    +
    + +
    +

    Далее мы рассмотрим механизм Go для группировки +и именования связанных наборов методов: интерфейсов.

    + +
    + + +
    +

    Следующий пример: Интерфейсы (Interfaces). @@ -34,7 +198,7 @@

    diff --git a/public/multiple-return-values b/public/multiple-return-values index fc6717b..fdce803 100644 --- a/public/multiple-return-values +++ b/public/multiple-return-values @@ -23,6 +23,141 @@

    Go в примерах: Функции с множественным возвратом (Multiple Return Values)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go имеет встроенную поддержку нескольких возвращаемых +значений. Эта особенность часто применяется в Go, +например, для возврата результата функции и ошибки.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    +

    Запись (int, int) в описании этой функции, говорит о +том, что функция возвращает два целых числа.

    + +
    + +
    func vals() (int, int) {
    +    return 3, 7
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Здесь функция возвращает два разных значения и +присваивает их переменным a,b. Это называется +множественное присваивание.

    + +
    + +
        a, b := vals()
    +    fmt.Println(a)
    +    fmt.Println(b)
    +
    + +
    +

    Если вы хотите получить не все значения, возвращаемые +функцией, то можно поспользоваться пустым +идентификатором _.

    + +
    + +
        _, c := vals()
    +    fmt.Println(c)
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run multiple-return-values.go
    +3
    +7
    +7
    +
    + +
    +

    Принятие переменного количества аргументов - +еще одна приятная особенность функций Go; +Рассмотрим это дальше.

    + +
    + + +
    +

    Следующий пример: Функции с переменным числом аргументов (Variadic Functions). @@ -34,7 +169,7 @@

    diff --git a/public/mutexes b/public/mutexes index 571bd6e..9574470 100644 --- a/public/mutexes +++ b/public/mutexes @@ -23,6 +23,270 @@

    Go в примерах: Мьютексы (Mutexes)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    В предыдущем примере мы увидели, как управлять простым +состоянием счетчика с помощью атомарных операций. +Для более сложного состояния мы можем использовать мьютекс(http://en.wikipedia.org/wiki/Mutual_exclusion) +для безопасного доступа к данным в нескольких горутинах.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "math/rand"
    +    "sync"
    +    "sync/atomic"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Для нашего примера state будет картой.

    + +
    + +
        var state = make(map[int]int)
    +
    + +
    +

    Этот mutex будет синхронизировать доступ к state.

    + +
    + +
        var mutex = &sync.Mutex{}
    +
    + +
    +

    Мы будем отслеживать, сколько операций чтения и +записи мы выполняем.

    + +
    + +
        var readOps uint64
    +    var writeOps uint64
    +
    + +
    +

    Здесь мы запускаем 100 горутин для выполнения +повторных операций чтения по состоянию, один раз +в миллисекунду в каждой горутине.

    + +
    + +
        for r := 0; r < 100; r++ {
    +        go func() {
    +            total := 0
    +            for {
    +
    + +
    +

    Для каждого чтения мы выбираем ключ для +доступа, блокируем mutex с помощью Lock() , +чтобы обеспечить исключительный доступ к +состоянию, читаем значение в выбранном ключе, +разблокируем мьютекс Unlock() и увеличиваем +количество readOps.

    + +
    + +
                    key := rand.Intn(5)
    +                mutex.Lock()
    +                total += state[key]
    +                mutex.Unlock()
    +                atomic.AddUint64(&readOps, 1)
    +
    + +
    +

    Немного ждем между чтениями.

    + +
    + +
                    time.Sleep(time.Millisecond)
    +            }
    +        }()
    +    }
    +
    + +
    +

    Запустим так же 10 горутин для симуляции записи, +так же как мы делали для чтения.

    + +
    + +
        for w := 0; w < 10; w++ {
    +        go func() {
    +            for {
    +                key := rand.Intn(5)
    +                val := rand.Intn(100)
    +                mutex.Lock()
    +                state[key] = val
    +                mutex.Unlock()
    +                atomic.AddUint64(&writeOps, 1)
    +                time.Sleep(time.Millisecond)
    +            }
    +        }()
    +    }
    +
    + +
    +

    Пусть 10 горутин работают над состоянием и +мьютексом на секунду.

    + +
    + +
        time.Sleep(time.Second)
    +
    + +
    +

    Смотрим финальное количество операций

    + +
    + +
        readOpsFinal := atomic.LoadUint64(&readOps)
    +    fmt.Println("readOps:", readOpsFinal)
    +    writeOpsFinal := atomic.LoadUint64(&writeOps)
    +    fmt.Println("writeOps:", writeOpsFinal)
    +
    + +
    +

    С окончательной блокировкой состояния смотрим, +как все закончилось.

    + +
    + +
        mutex.Lock()
    +    fmt.Println("state:", state)
    +    mutex.Unlock()
    +}
    +
    + +
    + + + + + + + + + + + + + +
    +

    Запуск программы показывает, что мы выполнили +около 90000 операций в нашем синхронизированном +с мьютексомсостоянии.

    + +
    + +
    $ go run mutexes.go
    +readOps: 83285
    +writeOps: 8320
    +state: map[1:97 4:53 0:33 2:15 3:2]
    +
    + +
    +

    Далее мы рассмотрим реализацию той же задачи управления +состоянием с использованием только горутин и каналов.

    + +
    + + +
    +

    Следующий пример: Управление состоянием горутин (Stateful Goroutines). @@ -34,7 +298,7 @@

    diff --git a/public/non-blocking-channel-operations b/public/non-blocking-channel-operations index 754f365..313bb01 100644 --- a/public/non-blocking-channel-operations +++ b/public/non-blocking-channel-operations @@ -23,6 +23,151 @@

    Go в примерах: Неблокируемые операции в каналах (Non-Blocking Channel Operations)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Стандартные отправители и получатели в каналах +являются блокирующими.Тем не менее, мы можем +использовать select с выбором по умолчанию для +реализации неблокирующих отправителей, получателей +и даже неблокирующего множественного select‘а.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    func main() {
    +    messages := make(chan string)
    +    signals := make(chan bool)
    +
    + +
    +

    Вот неблокирующее получение. Если значение +доступно в messages, тогда select выберет +<-messages с этим значением. Если нет, он +сразу же примет случай по-умолчанию.

    + +
    + +
        select {
    +    case msg := <-messages:
    +        fmt.Println("received message", msg)
    +    default:
    +        fmt.Println("no message received")
    +    }
    +
    + +
    +

    Неблокирующая отправка работает аналогично. +Здесь msg не может быть отправлено в канал +messages, потому что у канала нет буфера и +нет получателя. Поэтому выбирается действие +по-умолчанию.

    + +
    + +
        msg := "hi"
    +    select {
    +    case messages <- msg:
    +        fmt.Println("sent message", msg)
    +    default:
    +        fmt.Println("no message sent")
    +    }
    +
    + +
    +

    Мы можем использовать несколько case‘ов перед +действием по-умолчанию для реализации +многоцелевого неблокирующего выбора. Здесь мы +пытаемся получить неблокирующее получение +messages и signals.

    + +
    + +
        select {
    +    case msg := <-messages:
    +        fmt.Println("received message", msg)
    +    case sig := <-signals:
    +        fmt.Println("received signal", sig)
    +    default:
    +        fmt.Println("no activity")
    +    }
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run non-blocking-channel-operations.go 
    +no message received
    +no message sent
    +no activity
    +
    + +
    +

    Следующий пример: Закрытие каналов (Closing Channels). @@ -34,7 +179,7 @@

    diff --git a/public/number-parsing b/public/number-parsing index 1cf3b50..276c297 100644 --- a/public/number-parsing +++ b/public/number-parsing @@ -14,7 +14,7 @@ if (e.key == "ArrowRight") { - window.location.href = 'url'; + window.location.href = 'url-parsing'; } } @@ -23,9 +23,189 @@

    Go в примерах: Парсинг чисел (Number Parsing)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Парсинг чисел из строк является распространенной задачей; +Вот как это реализовать в Go.

    + +
    + + +
    + + + +
    package main
    +
    + +
    +

    Для решения этой задачи подойдет встроенный пакет +strconv.

    + +
    + +
    import (
    +    "fmt"
    +    "strconv"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    С помощью ParseFloat, параметр 64 говорит о том, +сколько битов точности необходимо использовать.

    + +
    + +
        f, _ := strconv.ParseFloat("1.234", 64)
    +    fmt.Println(f)
    +
    + +
    +

    Для ParseInt 0 означает вывод базы из строки. 64 +необходимо, чтобы результат соответствовал 64 битам.

    + +
    + +
        i, _ := strconv.ParseInt("123", 0, 64)
    +    fmt.Println(i)
    +
    + +
    +

    ParseInt будет распознавать числа в шестнадцатеричной +системе.

    + +
    + +
        d, _ := strconv.ParseInt("0x1c8", 0, 64)
    +    fmt.Println(d)
    +
    + +
    +

    ParseUint так же доступен.

    + +
    + +
        u, _ := strconv.ParseUint("789", 0, 64)
    +    fmt.Println(u)
    +
    + +
    +

    Atoi это удобная функция для парсинга в десятеричный +int.

    + +
    + +
        k, _ := strconv.Atoi("135")
    +    fmt.Println(k)
    +
    + +
    +

    Функции парсинга возвращают ошибку в случае некорректных +аргументов.

    + +
    + +
        _, e := strconv.Atoi("wat")
    +    fmt.Println(e)
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run number-parsing.go 
    +1.234
    +123
    +456
    +789
    +135
    +strconv.ParseInt: parsing "wat": invalid syntax
    +
    + +
    +

    Далее мы рассмотрим парсинг URL.

    + +
    + + +
    +

    - Следующий пример: Парсинг URL (URL Parsing). + Следующий пример: Парсинг URL (URL Parsing).

    diff --git a/public/pointers b/public/pointers index 37d75c0..e6f4ca7 100644 --- a/public/pointers +++ b/public/pointers @@ -23,6 +23,164 @@

    Go в примерах: Указатели (Pointers)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go поддерживает указатели, +позволяя вам передавать ссылки на значения и записи +в вашей программе.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    +

    Мы покажем, как работают указатели, на примере 2 функций: +zeroval и zeroptr. В zeroval определен только 1 +аргумент с типом int, который передается по значению. +zeroval получает копию ival при вызове функции.

    + +
    + +
    func zeroval(ival int) {
    +    ival = 0
    +}
    +
    + +
    +

    zeroptr получает в качестве аргумента параметр *int, +который является указателем на int. Запись *iptr в +теле функции разыменовывает указатель с его адреса +памяти на текущее значение по этому адресу. Присвоение +значения разыменованному указателю изменяет значение +по указанному адресу.

    + +
    + +
    func zeroptr(iptr *int) {
    +    *iptr = 0
    +}
    +
    + +
    + + + +
    func main() {
    +    i := 1
    +    fmt.Println("initial:", i)
    +
    + +
    + + + +
        zeroval(i)
    +    fmt.Println("zeroval:", i)
    +
    + +
    +

    Запись &i получается ссылку на область памяти, в +которой хранится i, т.е. указатель на i.

    + +
    + +
        zeroptr(&i)
    +    fmt.Println("zeroptr:", i)
    +
    + +
    +

    Указатели могут быть выведены на экран

    + +
    + +
        fmt.Println("pointer:", &i)
    +}
    +
    + +
    + + + + + + + + +
    +

    zeroval не изменяет значение i в main, но +zeroptr изменяет, т.к. в него передается указатель +на область памяти, в которой хранится переменная.

    + +
    + +
    $ go run pointers.go
    +initial: 1
    +zeroval: 1
    +zeroptr: 0
    +pointer: 0x42131100
    +
    + +
    +

    Следующий пример: Структуры (Structs). @@ -34,7 +192,7 @@

    diff --git a/public/random-numbers b/public/random-numbers index eb1856a..cc419d2 100644 --- a/public/random-numbers +++ b/public/random-numbers @@ -23,6 +23,204 @@

    Go в примерах: Случайные числа (Random Numbers)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Пакет Go math/rand обеспечивает генерацию +псевдослучайных чисел.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "math/rand"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Например, rand.Intn вернет случайное целое число в +диапазоне 0 <= n < 100.

    + +
    + +
        fmt.Print(rand.Intn(100), ",")
    +    fmt.Print(rand.Intn(100))
    +    fmt.Println()
    +
    + +
    +

    rand.Float64 вернет число с плавающей точкой в присвоит +его в f, где f будет в промежутке 0.0 <= f < 1.0.

    + +
    + +
        fmt.Println(rand.Float64())
    +
    + +
    +

    Этот способ может быть использован для генерации других +случайных чисел с плавающей точкой в разных диапазонах, +например для диапазона 5.0 <= f' < 10.0.

    + +
    + +
        fmt.Print((rand.Float64()*5)+5, ",")
    +    fmt.Print((rand.Float64() * 5) + 5)
    +    fmt.Println()
    +
    + +
    +

    Генератор чисел по умолчанию является детерминированным, +поэтому по умолчанию он будет каждый раз генерировать +одну и ту же последовательность чисел. Чтобы произвести +различные последовательности, дайте ему соль(https://ru.wikipedia.org/wiki/Соль_(криптография)), +которая изменяется. Обратите внимание, что это не +безопасно использовать для генерации случайных чисел, +которые вы намерены хранить в секрете, используйте +crypto/rand для них.

    + +
    + +
        s1 := rand.NewSource(time.Now().UnixNano())
    +    r1 := rand.New(s1)
    +
    + +
    +

    Вызвать результат rand.Rand можно точно так же, как и +функции в пакете rand.

    + +
    + +
        fmt.Print(r1.Intn(100), ",")
    +    fmt.Print(r1.Intn(100))
    +    fmt.Println()
    +
    + +
    +

    Если вы используете одну и ту же соль для генерации +чисел, то они будут совпадать.

    + +
    + +
        s2 := rand.NewSource(42)
    +    r2 := rand.New(s2)
    +    fmt.Print(r2.Intn(100), ",")
    +    fmt.Print(r2.Intn(100))
    +    fmt.Println()
    +    s3 := rand.NewSource(42)
    +    r3 := rand.New(s3)
    +    fmt.Print(r3.Intn(100), ",")
    +    fmt.Print(r3.Intn(100))
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run random-numbers.go
    +81,87
    +0.6645600532184904
    +7.123187485356329,8.434115364335547
    +0,28
    +5,87
    +5,87
    +
    + +
    +

    Обратитесь к документации по пакету math/rand +для ознакомления с другими случайными величинами, +которые может предоставить Go.

    + +
    + + +
    +

    Следующий пример: Парсинг чисел (Number Parsing). @@ -34,7 +232,7 @@

    diff --git a/public/range b/public/range index 17e52bb..043d3ac 100644 --- a/public/range +++ b/public/range @@ -23,6 +23,174 @@

    Go в примерах: Ряд (Range)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    range перебирает элементы в различных структурах +данных. Давайте посмотрим, как использовать +range с некоторыми из структур данных, которые +мы уже изучили.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    В данном примере мы используем range для +подсчета суммы чисел в срезе. +Для массива синтаксис будет такой же.

    + +
    + +
        nums := []int{2, 3, 4}
    +    sum := 0
    +    for _, num := range nums {
    +        sum += num
    +    }
    +    fmt.Println("sum:", sum)
    +
    + +
    +

    range для массивов и срезов возвращает индекс +и значение для каждого элемента. Если нам не +требуется индекс, мы можем использовать оператор +_ для игнорирования. Иногда нам действительно +необходимы индексы.

    + +
    + +
        for i, num := range nums {
    +        if num == 3 {
    +            fmt.Println("index:", i)
    +        }
    +    }
    +
    + +
    +

    range для карт перебирает пары ключ/значение.

    + +
    + +
        kvs := map[string]string{"a": "apple", "b": "banana"}
    +    for k, v := range kvs {
    +        fmt.Printf("%s -> %s\n", k, v)
    +    }
    +
    + +
    +

    range может перебирать только ключи в карте

    + +
    + +
        for k := range kvs {
    +        fmt.Println("key:", k)
    +    }
    +
    + +
    +

    range для строк перебирает кодовые точки Unicode. +Первое значение - это начальный байтовый индекс +руны, а второе - сама руна.

    + +
    + +
        for i, c := range "go" {
    +        fmt.Println(i, c)
    +    }
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run range.go
    +sum: 9
    +index: 1
    +a -> apple
    +b -> banana
    +key: a
    +key: b
    +0 103
    +1 111
    +
    + +
    +

    Следующий пример: Функции (Functions). @@ -34,7 +202,7 @@

    diff --git a/public/range-over-channels b/public/range-over-channels index e9724c5..24b1cc2 100644 --- a/public/range-over-channels +++ b/public/range-over-channels @@ -23,6 +23,125 @@

    Go в примерах: Перебор значений из каналов (Range over Channels)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    В предыдущем примере мы виделе как for и +range позволяют перебирать базовые структуры. Мы +так же можем использовать этот синтаксис для чтения +значений из канала.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Мы будем итерировать 2 значения в канале queue.

    + +
    + +
        queue := make(chan string, 2)
    +    queue <- "one"
    +    queue <- "two"
    +    close(queue)
    +
    + +
    +

    Этот range будет перебирать каждый элемент +полученный из канала queue. Но т.к. мы закрыли +канал ранее, перебор элементов завершится после +получения двух элементов.

    + +
    + +
        for elem := range queue {
    +        fmt.Println(elem)
    +    }
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run range-over-channels.go
    +one
    +two
    +
    + +
    +

    Этот пример так же демонстрирует, что возможно +прочитать данные из канала уже после его закрытия.

    + +
    + + +
    +

    Следующий пример: Таймеры (Timers). @@ -34,7 +153,7 @@

    diff --git a/public/rate-limiting b/public/rate-limiting index e55807e..24eeca4 100644 --- a/public/rate-limiting +++ b/public/rate-limiting @@ -23,6 +23,235 @@

    Go в примерах: Ограничение скорости (Rate Limiting)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Ограничение скорости +является важным механизмом контроля использования ресурсов и +поддержания качества обслуживания. Go элегантно поддерживает +ограничение скорости с помощью горутин, каналов и тикеров.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Сначала мы рассмотрим базовое ограничение скорости. +Предположим, мы хотим ограничить нашу обработку +входящих запросов. Мы будем обслуживать эти запросы +с одноименного канала.

    + +
    + +
        requests := make(chan int, 5)
    +    for i := 1; i <= 5; i++ {
    +        requests <- i
    +    }
    +    close(requests)
    +
    + +
    +

    Канал limiter будет получать значение каждые +200мс. Это то, что регулирует скорость в нашей +схеме.

    + +
    + +
        limiter := time.Tick(200 * time.Millisecond)
    +
    + +
    +

    Блокируя прием от канала limiter перед +обслуживанием каждого запроса, мы ограничиваем себя +одним запросом каждые 200 миллисекунд.

    + +
    + +
        for req := range requests {
    +        <-limiter
    +        fmt.Println("request", req, time.Now())
    +    }
    +
    + +
    +

    Мы можем разрешить короткие всплески запросов в +нашей схеме ограничения скорости при сохранении +общего ограничения скорости. Мы можем сделать это +путем буферизации нашего канала ограничения. Этот +канал burstyLimiter будет позволять делать до +3 событий.

    + +
    + +
        burstyLimiter := make(chan time.Time, 3)
    +
    + +
    +

    Заполняем канал, чтобы предоставить возможность +ускорить.

    + +
    + +
        for i := 0; i < 3; i++ {
    +        burstyLimiter <- time.Now()
    +    }
    +
    + +
    +

    Каждые 200мс мы будем пытаться добавлять новое +значение в burstyLimiter, до своего предела +в 3 значения.

    + +
    + +
        go func() {
    +        for t := range time.Tick(200 * time.Millisecond) {
    +            burstyLimiter <- t
    +        }
    +    }()
    +
    + +
    +

    Теперь смоделируем еще 5 входящих запросов. Первые +3 из них получат выгоду от вместимости burstyLimiter.

    + +
    + +
        burstyRequests := make(chan int, 5)
    +    for i := 1; i <= 5; i++ {
    +        burstyRequests <- i
    +    }
    +    close(burstyRequests)
    +    for req := range burstyRequests {
    +        <-burstyLimiter
    +        fmt.Println("request", req, time.Now())
    +    }
    +}
    +
    + +
    + + + + + + + + + + + + + +
    +

    При запуске нашей программы мы видим, что первая +партия запросов обрабатывается каждые ~200мс.

    + +
    + +
    $ go run rate-limiting.go
    +request 1 2012-10-19 00:38:18.687438 +0000 UTC
    +request 2 2012-10-19 00:38:18.887471 +0000 UTC
    +request 3 2012-10-19 00:38:19.087238 +0000 UTC
    +request 4 2012-10-19 00:38:19.287338 +0000 UTC
    +request 5 2012-10-19 00:38:19.487331 +0000 UTC
    +
    + +
    +

    Для второго пула запросов мы обслуживаем первые +3 сразу из-за использования ограничения скорости, +затем обслуживаем оставшиеся 2 с задержками ~200мс +каждый.

    + +
    + +
    request 1 2012-10-19 00:38:20.487578 +0000 UTC
    +request 2 2012-10-19 00:38:20.487645 +0000 UTC
    +request 3 2012-10-19 00:38:20.487676 +0000 UTC
    +request 4 2012-10-19 00:38:20.687483 +0000 UTC
    +request 5 2012-10-19 00:38:20.887542 +0000 UTC
    +
    + +
    +

    Следующий пример: Атомарные счетчики (Atomic Counters). @@ -34,7 +263,7 @@

    diff --git a/public/reading-files b/public/reading-files index eeab811..802617d 100644 --- a/public/reading-files +++ b/public/reading-files @@ -9,7 +9,7 @@ onkeydown = (e) => { if (e.key == "ArrowLeft") { - window.location.href = 'base64'; + window.location.href = 'base64-encoding'; } @@ -23,6 +23,261 @@

    Go в примерах: Чтение файлов (Reading Files)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Чтение и запись файлов это базовая задача, необходимая +для решения множества задач. Для начала мы рассмотрим +несколько примеров чтения файлов.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "bufio"
    +    "fmt"
    +    "io"
    +    "io/ioutil"
    +    "os"
    +)
    +
    + +
    +

    Чтение файлов требует проверок множества вызовов на +наличие ошибок. Эта функция-хелпер поможет нам +обрабатывать ошибки в одном месте.

    + +
    + +
    func check(e error) {
    +    if e != nil {
    +        panic(e)
    +    }
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Возможно, самая основная задача чтения файлов - +это сохранение всего содержимого файла в памяти.

    + +
    + +
        dat, err := ioutil.ReadFile("/tmp/dat")
    +    check(err)
    +    fmt.Print(string(dat))
    +
    + +
    +

    Вам часто может потребоваться больший контроль +над тем, как и какие части файла читаются. Для +решения этих задач начните с открытия файла, +чтобы получить значение os.File..

    + +
    + +
        f, err := os.Open("/tmp/dat")
    +    check(err)
    +
    + +
    +

    Прочитаем несколько байт с начала файла. Будет +прочитано первые 5 байт, но также выведем, +сколько фактически было прочитано.

    + +
    + +
        b1 := make([]byte, 5)
    +    n1, err := f.Read(b1)
    +    check(err)
    +    fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))
    +
    + +
    +

    Вы так же можете получить конкретное место файла +с помощью Seek и выполнить Read оттуда.

    + +
    + +
        o2, err := f.Seek(6, 0)
    +    check(err)
    +    b2 := make([]byte, 2)
    +    n2, err := f.Read(b2)
    +    check(err)
    +    fmt.Printf("%d bytes @ %d: ", n2, o2)
    +    fmt.Printf("%v\n", string(b2[:n2]))
    +
    + +
    +

    Пакет io предоставляет некоторые функции, которые +могут быть полезны для чтения файлов. Например, +чтение, подобное приведенному выше, может быть +более надежно реализовано с помощью ReadAtLeast.

    + +
    + +
        o3, err := f.Seek(6, 0)
    +    check(err)
    +    b3 := make([]byte, 2)
    +    n3, err := io.ReadAtLeast(f, b3, 2)
    +    check(err)
    +    fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))
    +
    + +
    +

    Тут нет встроенной перемотки назад, но можно +использовать Seek(0, 0) для этого.

    + +
    + +
        _, err = f.Seek(0, 0)
    +    check(err)
    +
    + +
    +

    В пакете bufio реализован буферизованный ридер, +который может быть полезен из-за своей эффективности +при большом количестве небольших операций чтения, и +из-за наличия дополнительных методов чтения, которые +он предоставляет.

    + +
    + +
        r4 := bufio.NewReader(f)
    +    b4, err := r4.Peek(5)
    +    check(err)
    +    fmt.Printf("5 bytes: %s\n", string(b4))
    +
    + +
    +

    Закройте файл, когда вы закончите использовать его +(обычно закрытие с defer‘ом делается сразу после открытия).

    + +
    + +
        f.Close()
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ echo "hello" > /tmp/dat
    +$ echo "go" >>   /tmp/dat
    +$ go run reading-files.go
    +hello
    +go
    +5 bytes: hello
    +2 bytes @ 6: go
    +2 bytes @ 6: go
    +5 bytes: hello
    +
    + +
    +

    Далее рассмотрим запись в файл.

    + +
    + + +
    +

    Следующий пример: Запись файлов (Writing Files). @@ -34,7 +289,7 @@

    diff --git a/public/recursion b/public/recursion index a7bf95e..3db9aa5 100644 --- a/public/recursion +++ b/public/recursion @@ -23,6 +23,97 @@

    Go в примерах: Рекурсия (Recursion)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go поддерживает +рекурсивные функции. +Ниже приведено классическое вычисление факториала.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    +

    Фукция fact вызывает себя по не достигнет +fact(0).

    + +
    + +
    func fact(n int) int {
    +    if n == 0 {
    +        return 1
    +    }
    +    return n * fact(n-1)
    +}
    +
    + +
    + + + +
    func main() {
    +    fmt.Println(fact(7))
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run recursion.go 
    +5040
    +
    + +
    +

    Следующий пример: Указатели (Pointers). @@ -34,7 +125,7 @@

    diff --git a/public/regular-expressions b/public/regular-expressions index 700db55..1f38c4b 100644 --- a/public/regular-expressions +++ b/public/regular-expressions @@ -23,6 +23,317 @@

    Go в примерах: Регулярные выражения (Regular Expressions)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go предлагает встроенную поддержку регулярных выражений. +Вот несколько примеров, связанных с регулярными +выражениями в Go.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "bytes"
    +    "fmt"
    +    "regexp"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Проверяем, соответствует ли шаблон строке

    + +
    + +
        match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
    +    fmt.Println(match)
    +
    + +
    +

    Выше мы использовали строковый шаблон напрямую, +но для других задач с регулярными выражениями, +необходимо скомпилировать оптимизированную +структуру Regexp.

    + +
    + +
        r, _ := regexp.Compile("p([a-z]+)ch")
    +
    + +
    +

    Множество методов доступны для этой структуры. +Вот тест на совпадение, который мы видели ранее.

    + +
    + +
        fmt.Println(r.MatchString("peach"))
    +
    + +
    +

    Этот метод находит соответствие для регулярного +выражения.

    + +
    + +
        fmt.Println(r.FindString("peach punch"))
    +
    + +
    +

    Этот метод также находит первое совпадение, но +возвращает начальный и конечный индексы совпадения +вместо текста.

    + +
    + +
        fmt.Println(r.FindStringIndex("peach punch"))
    +
    + +
    +

    Варианты Submatch включают в себя информацию +как о совпадениях с полным шаблоном, так и о +совпадениях с частями шаблона. Например, эта +конструкция вернет информацию как +для p([a-z]+)ch, так и для ([a-z]+).

    + +
    + +
        fmt.Println(r.FindStringSubmatch("peach punch"))
    +
    + +
    +

    Точно так же это возвратит информацию об индексах +совпадений и подсовпадений.

    + +
    + +
        fmt.Println(r.FindStringSubmatchIndex("peach punch"))
    +
    + +
    +

    Метод All применяется ко всем совпадениям на входе, +а не только к первому. Например, чтобы найти все +совпадения для регулярного выражения.

    + +
    + +
        fmt.Println(r.FindAllString("peach punch pinch", -1))
    +
    + +
    +

    Этот метод All доступен и для других функций, +которые мы видели выше.

    + +
    + +
        fmt.Println(r.FindAllStringSubmatchIndex(
    +        "peach punch pinch", -1))
    +
    + +
    +

    Указание неотрицательного целого числа в качестве +второго аргумента для этих функций ограничит +количество совпадений.

    + +
    + +
        fmt.Println(r.FindAllString("peach punch pinch", 2))
    +
    + +
    +

    В наших примерах выше были строковые аргументы и +использовались такие имена, как MatchString. Мы +также можем предоставить []byte аргументы и удалить +String из имени функции.

    + +
    + +
        fmt.Println(r.Match([]byte("peach")))
    +
    + +
    +

    При создании констант с регулярными выражениями +вы можете использовать MustCompile, как аналог +Compile. Обычный Compile не будет работать +для констант, потому что он возвращает 2 значения.

    + +
    + +
        r = regexp.MustCompile("p([a-z]+)ch")
    +    fmt.Println(r)
    +
    + +
    +

    Пакет regexp также можно использовать для +замены подмножеств строк другими значениями.

    + +
    + +
        fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))
    +
    + +
    +

    Вариант с Func позволяет вам преобразовывать +сопоставленный текст с заданной функцией.

    + +
    + +
        in := []byte("a peach")
    +    out := r.ReplaceAllFunc(in, bytes.ToUpper)
    +    fmt.Println(string(out))
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run regular-expressions.go 
    +true
    +true
    +peach
    +[0 5]
    +[peach ea]
    +[0 5 1 3]
    +[peach punch pinch]
    +[[0 5 1 3] [6 11 7 9] [12 17 13 15]]
    +[peach punch]
    +true
    +p([a-z]+)ch
    +a <fruit>
    +a PEACH
    +
    + +
    +

    Для получения полной ссылки на регулярные выражения Go +проверьте документацию пакета regexp.

    + +
    + + +
    +

    Следующий пример: JSON. @@ -34,7 +345,7 @@

    diff --git a/public/sha1 b/public/sha1 deleted file mode 100644 index daf6a09..0000000 --- a/public/sha1 +++ /dev/null @@ -1,41 +0,0 @@ - - - - - Go в примерах: Хеш SHA1 (SHA1 Hashes) - - - - -
    -

    Go в примерах: Хеш SHA1 (SHA1 Hashes)

    - - -

    - Следующий пример: Кодирование Base64 (Base64 Encoding). -

    - - -
    - - - - diff --git a/public/sha1-hashes b/public/sha1-hashes new file mode 100644 index 0000000..5ae7bdc --- /dev/null +++ b/public/sha1-hashes @@ -0,0 +1,212 @@ + + + + + Go в примерах: Хеш SHA1 (SHA1 Hashes) + + + + +
    +

    Go в примерах: Хеш SHA1 (SHA1 Hashes)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Хеши SHA1 часто +используются для вычисления коротких идентификаторов +для двоичных или текстовых BLOB-объектов. Например, +система контроля версий git широко использует SHA1 для +идентификации версионных файлов и каталогов. Вот как +вычислить хэши SHA1 в Go.

    + +
    + + +
    + + + +
    package main
    +
    + +
    +

    Go реализует несколько хеш-функций в пакете +crypto/*.

    + +
    + +
    import (
    +    "crypto/sha1"
    +    "fmt"
    +)
    +
    + +
    + + + +
    func main() {
    +    s := "sha1 this string"
    +
    + +
    +

    Для генерации хеша используется функция sha1.New(), +sha1.Write(bytes), затем sha1.Sum([]byte{}). +Тут мы создаем новый хеш.

    + +
    + +
        h := sha1.New()
    +
    + +
    +

    В Write необходимо передать байты. Если у вас есть +строка s, используйте []byte(s), чтобы привести +ее к байтам.

    + +
    + +
        h.Write([]byte(s))
    +
    + +
    +

    Получаем окончательный результат хеш-функции в виде +байтового фрагмента. Аргумент в Sum добавляет к +существующему фрагменту байты, но обычно он не используется.

    + +
    + +
        bs := h.Sum(nil)
    +
    + +
    +

    Значения SHA1 часто печатаются в шестнадцатеричном формате, +например, в git commit. Используйте %x для преобразования +результатов хеширования в шестнадцатеричную строку.

    + +
    + +
        fmt.Println(s)
    +    fmt.Printf("%x\n", bs)
    +}
    +
    + +
    + + + + + + + + + + + + + + + + + + +
    +

    Запущенная программа вычисляет хеш и печатает его в +удобочитаемом шестнадцатеричном формате.

    + +
    + +
    $ go run sha1-hashes.go
    +sha1 this string
    +cf23df2207d99a74fbe169e3eba035e633b65d94
    +
    + +
    +

    Вы можете вычислить другие хэши, используя шаблон, +аналогичный показанному выше. Например, для вычисления +хэшей MD5 импортируйте crypto/md5 и используйте +md5.New().

    + +
    + + +
    +

    Обратите внимание, что если вам нужны криптографически +защищенные хэши, вы должны тщательно исследовать +стойкость хэша!

    + +
    + + +
    + + +

    + Следующий пример: Кодирование Base64 (Base64 Encoding). +

    + + +
    + + + + diff --git a/public/signals b/public/signals index d4b3432..639ad78 100644 --- a/public/signals +++ b/public/signals @@ -23,6 +23,164 @@

    Go в примерах: Сигналы (Signals)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Иногда нам хотелось бы, чтобы наши программы на Go +интеллектуально обрабатывали сигналы Unix. +Например, мы можем захотеть, чтобы сервер корректно +завершил работу при получении SIGTERM, или инструмент +командной строки остановил обработку ввода, если он +получил SIGINT. Вот как обрабатывать сигналы в Go +с каналами.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "os"
    +    "os/signal"
    +    "syscall"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Уведомление о выходе сигнала работает путем +отправки значений os.Signal в канал. Мы создадим +канал для получения этих уведомлений (мы также +создадим канал, чтобы уведомить нас, когда программа +может выйти).

    + +
    + +
        sigs := make(chan os.Signal, 1)
    +    done := make(chan bool, 1)
    +
    + +
    +

    signal.Notify регистрирует данный канал для +получения уведомлений об указанных сигналах.

    + +
    + +
        signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
    +
    + +
    +

    Эта горутина выполняет блокировку приема сигналов. +Когда она получит его, то распечатает его, а +затем уведомит программу, что она может быть завершена.

    + +
    + +
        go func() {
    +        sig := <-sigs
    +        fmt.Println()
    +        fmt.Println(sig)
    +        done <- true
    +    }()
    +
    + +
    +

    Программа будет ждать здесь, пока не получит +ожидаемый сигнал (как указано в приведенной +выше процедуре, отправляющей значение в done), +и затем завершится.

    + +
    + +
        fmt.Println("awaiting signal")
    +    <-done
    +    fmt.Println("exiting")
    +}
    +
    + +
    + + + + + + + + +
    +

    Когда мы запустим эту программу, она заблокирует +ожидание сигнала. Набрав ctrl-C (который терминал +показывает как ^C), мы можем послать сигнал SIGINT, +в результате чего программа напечатает interrupt +и затем выйдет.

    + +
    + +
    $ go run signals.go
    +awaiting signal
    +^C
    +interrupt
    +exiting
    +
    + +
    +

    Следующий пример: Выход (Exit). @@ -34,7 +192,7 @@

    diff --git a/public/site.js b/public/site.js index 752e656..e2539fb 100644 --- a/public/site.js +++ b/public/site.js @@ -1,17 +1,17 @@ -/*! - * clipboard.js v1.5.13 - * https://zenorocha.github.io/clipboard.js - * - * Licensed MIT © Zeno Rocha - */ -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,o){function r(c,a){if(!n[c]){if(!e[c]){var l="function"==typeof require&&require;if(!a&&l)return l(c,!0);if(i)return i(c,!0);var s=new Error("Cannot find module '"+c+"'");throw s.code="MODULE_NOT_FOUND",s}var u=n[c]={exports:{}};e[c][0].call(u.exports,function(t){var n=e[c][1][t];return r(n?n:t)},u,u.exports,t,e,n,o)}return n[c].exports}for(var i="function"==typeof require&&require,c=0;c

    Go в примерах: Срезы (Slices)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Срезы являются ключевым типом данных в Go, +предоставляя более мощный интерфейс для последовательностей, +чем массивы.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    В отличии от массивов, длина среза зависит от содержащихся +в срезе элементов, а не определена при инициализации. +Создать пустой срез в ненулевой длиной можно используя +оператор make. В этом пример мы создаем слайс строк +длиной 3 (заполненный нулевыми значениями).

    + +
    + +
        s := make([]string, 3)
    +    fmt.Println("emp:", s)
    +
    + +
    +

    Мы можем устанавливать и получать значения, как в массивах.

    + +
    + +
        s[0] = "a"
    +    s[1] = "b"
    +    s[2] = "c"
    +    fmt.Println("set:", s)
    +    fmt.Println("get:", s[2])
    +
    + +
    +

    len возвращает длину среза, как и ожидалось.

    + +
    + +
        fmt.Println("len:", len(s))
    +
    + +
    +

    В дополнение к базовой функциональности, срезы +имеют несколько дополнительных особенностей +по сравнению с массивыми. Одна из них - append, +которая возвращает срезу содержащий одно или более +новых значений. Обратите внимание, что результат +функции append необходимо присвоить в переменную, +т.к. это уже будет новый срез.

    + +
    + +
        s = append(s, "d")
    +    s = append(s, "e", "f")
    +    fmt.Println("apd:", s)
    +
    + +
    +

    Срезы могут быть скопированы с помощью copy. В +данном примере мы создаем пустой срез c такой же +длины как и s и копируем данные из s в c.

    + +
    + +
        c := make([]string, len(s))
    +    copy(c, s)
    +    fmt.Println("cpy:", c)
    +
    + +
    +

    Срезы поддерживают оператор slice (синтаксис +использование slice[low:high]). Для примера, +тут мы получаем срез состоящий из элементов +s[2], s[3], и s[4].

    + +
    + +
        l := s[2:5]
    +    fmt.Println("sl1:", l)
    +
    + +
    +

    Тут мы получаем срез до элемента s[5] (исключая его).

    + +
    + +
        l = s[:5]
    +    fmt.Println("sl2:", l)
    +
    + +
    +

    А тут получаем срез от s[2] (включая его) и до конца +исходного среза.

    + +
    + +
        l = s[2:]
    +    fmt.Println("sl3:", l)
    +
    + +
    +

    Мы можем объявить и заполнить срез значениями в одну +строку.

    + +
    + +
        t := []string{"g", "h", "i"}
    +    fmt.Println("dcl:", t)
    +
    + +
    +

    Срезы можно объединять в многомерные структуры +данных. Длина внутренних срезов может варьироваться, +в отличии от многомерных массивов.

    + +
    + +
        twoD := make([][]int, 3)
    +    for i := 0; i < 3; i++ {
    +        innerLen := i + 1
    +        twoD[i] = make([]int, innerLen)
    +        for j := 0; j < innerLen; j++ {
    +            twoD[i][j] = i + j
    +        }
    +    }
    +    fmt.Println("2d: ", twoD)
    +}
    +
    + +
    + + + + + + + + + + + + + + + + + + +
    +

    Обратите внимание, несмотря на то что срезы +являются отдельным типом данных, отображаются +они так же как массивы командой fmt.Println.

    + +
    + +
    $ go run slices.go
    +emp: [  ]
    +set: [a b c]
    +get: c
    +len: 3
    +apd: [a b c d e f]
    +cpy: [a b c d e f]
    +sl1: [c d e]
    +sl2: [a b c d e]
    +sl3: [c d e f]
    +dcl: [g h i]
    +2d:  [[0] [1 2] [2 3 4]]
    +
    + +
    +

    Посмотрите этот отличный пост, +написаный командой Go, чтобы узнать больше о разработке +и использовании срезов в Go.

    + +
    + + +
    +

    Теперь, когда мы рассмотрели массивы и срезы, мы +посмотрим другую ключевую встроенную структуру +данных Go: карты.

    + +
    + + +
    +

    Следующий пример: Карты (Maps). @@ -34,7 +314,7 @@ diff --git a/public/sorting b/public/sorting index 3cc8fc4..80cd209 100644 --- a/public/sorting +++ b/public/sorting @@ -23,6 +23,133 @@

    Go в примерах: Сортировка (Sorting)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Пакет sort реализует сортировку для встроенных и +пользовательских типов. Сначала рассмотрим сортировку +встроенных типов.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "sort"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Методы сортировки специфичны для встроенного типа; +Вот пример для строк. Обратите внимание, что +сортировка выполняется на месте, поэтому она +изменяет данный фрагмент и не возвращает новый.

    + +
    + +
        strs := []string{"c", "a", "b"}
    +    sort.Strings(strs)
    +    fmt.Println("Strings:", strs)
    +
    + +
    +

    Пример сортировки int‘ов

    + +
    + +
        ints := []int{7, 2, 4}
    +    sort.Ints(ints)
    +    fmt.Println("Ints:   ", ints)
    +
    + +
    +

    Мы так же можем использовать sort, для +проверки, что срез был уже отсортирован.

    + +
    + +
        s := sort.IntsAreSorted(ints)
    +    fmt.Println("Sorted: ", s)
    +}
    +
    + +
    + + + + + + + + +
    +

    После запуска наша программа выведет отсортированные +строки и срез целых чисел и true, как результат +выполнения AreSorted.

    + +
    + +
    $ go run sorting.go
    +Strings: [a b c]
    +Ints:    [2 4 7]
    +Sorted:  true
    +
    + +
    +

    Следующий пример: Сортировка через функции (Sorting by Functions). @@ -34,7 +161,7 @@

    diff --git a/public/sorting-by-functions b/public/sorting-by-functions index fb1f2fa..f4e2df7 100644 --- a/public/sorting-by-functions +++ b/public/sorting-by-functions @@ -23,6 +23,150 @@

    Go в примерах: Сортировка через функции (Sorting by Functions)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Иногда мы хотим отсортировать коллекцию по какому-то +другому признаку, кроме ее естественного порядка. +Например, предположим, что мы хотели бы отсортировать +строки по длине, а не по алфавиту. Вот пример +пользовательских сортировок в Go.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "sort"
    +)
    +
    + +
    +

    Для сортировки по пользовательской функции в Go нам +нужен соответствующий тип. Здесь мы создали тип +byLength, который является просто псевдонимом для +[]string.

    + +
    + +
    type byLength []string
    +
    + +
    +

    Мы реализуем sort.Interface - Len,LessиSwap +- для нашего типа, чтобы мы могли использовать общую +функциюSortпакетаsort.LenиSwapобычно +одинаковы для разных типов, аLessбудет содержать +реальную пользовательскую логику сортировки. В нашем +случае мы хотим отсортировать в порядке увеличения +длины строки, поэтому мы используемlen(s[i])и +len(s[j])` здесь.

    + +
    + +
    func (s byLength) Len() int {
    +    return len(s)
    +}
    +func (s byLength) Swap(i, j int) {
    +    s[i], s[j] = s[j], s[i]
    +}
    +func (s byLength) Less(i, j int) bool {
    +    return len(s[i]) < len(s[j])
    +}
    +
    + +
    +

    Реализовав интерфейс, мы можем теперь реализовать +нашу собственную сортировку, преобразовав исходный +срез fruits в byLength, а затем использовать +sort.Sort для этого типизированного среза.

    + +
    + +
    func main() {
    +    fruits := []string{"peach", "banana", "kiwi"}
    +    sort.Sort(byLength(fruits))
    +    fmt.Println(fruits)
    +}
    +
    + +
    + + + + + + + + + + + + + +
    +

    При запуске нашей программы отображается список, +отсортированный по длине строки, как мы и хотели.

    + +
    + +
    $ go run sorting-by-functions.go 
    +[kiwi peach banana]
    +
    + +
    +

    Следуя той же схеме создания пользовательского типа, +реализации трех методов интерфейса для этого типа +и последующего вызова sort.Sort для коллекции +этого типа, мы можем сортировать срезы Go +по произвольным функциям.

    + +
    + + +
    +

    Следующий пример: Panic. @@ -34,7 +178,7 @@

    diff --git a/public/spawning-processes b/public/spawning-processes index 1cc1f34..611921e 100644 --- a/public/spawning-processes +++ b/public/spawning-processes @@ -9,7 +9,7 @@ onkeydown = (e) => { if (e.key == "ArrowLeft") { - window.location.href = 'http'; + window.location.href = 'http-servers'; } @@ -23,6 +23,232 @@

    Go в примерах: Порождающие процессы (Spawning Processes)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Иногда наши программы Go должны порождать другие, не +Go процессы. Например, подсветка синтаксиса на этом +сайте реализуется +путем запуска pygmentize +процесса из программы Go. Давайте рассмотрим несколько +примеров порождающих процессов из Go.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "io/ioutil"
    +    "os/exec"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Мы начнем с простой команды, которая не принимает +аргументов или ввода и просто печатает что-то на +стандартный вывод. Хелпер exec.Command создает +объект для представления этого внешнего процесса.

    + +
    + +
        dateCmd := exec.Command("date")
    +
    + +
    +

    .Output - это еще один хелпер, который обрабатывает +общий случай запуска команды, ожидаетее завершения +и сбора выходных данных. Если ошибок не было, dateOut +будет содержать байты с информацией о дате.

    + +
    + +
        dateOut, err := dateCmd.Output()
    +    if err != nil {
    +        panic(err)
    +    }
    +    fmt.Println("> date")
    +    fmt.Println(string(dateOut))
    +
    + +
    +

    Далее мы рассмотрим несколько более сложный случай, +когда мы направляем данные во внешний процесс на +его stdin и собираем результаты из его stdout.

    + +
    + +
        grepCmd := exec.Command("grep", "hello")
    +
    + +
    +

    Здесь мы явно получаем каналы ввода-вывода, +запускаем процесс, записываем в него некоторые +входные данные, читаем полученный результат и, +наконец, ожидаем завершения процесса.

    + +
    + +
        grepIn, _ := grepCmd.StdinPipe()
    +    grepOut, _ := grepCmd.StdoutPipe()
    +    grepCmd.Start()
    +    grepIn.Write([]byte("hello grep\ngoodbye grep"))
    +    grepIn.Close()
    +    grepBytes, _ := ioutil.ReadAll(grepOut)
    +    grepCmd.Wait()
    +
    + +
    +

    Мы опускаем проверки ошибок в приведенном выше +примере, но вы можете использовать обычный шаблон +if err != nil для них. Мы также собираем только +результаты StdoutPipe, но вы можете собирать +StderrPipe точно таким же образом.

    + +
    + +
        fmt.Println("> grep hello")
    +    fmt.Println(string(grepBytes))
    +
    + +
    +

    Обратите внимание, что при порождении команд нам +нужно предоставить явно разграниченный массив +команд и аргументов вместо возможности просто +передать одну строку командной строки. +Если вы хотите создать полную команду со строкой, +вы можете использовать опцию -c в bash:

    + +
    + +
        lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
    +    lsOut, err := lsCmd.Output()
    +    if err != nil {
    +        panic(err)
    +    }
    +    fmt.Println("> ls -a -l -h")
    +    fmt.Println(string(lsOut))
    +}
    +
    + +
    + + + + + + + + + + + + + + + + + + +
    +

    Порожденные программы возвращают вывод, который +является таким же, как если бы мы запускали их +непосредственно из командной строки.

    + +
    + +
    $ go run spawning-processes.go 
    +> date
    +Wed Oct 10 09:53:11 PDT 2012
    +
    + +
    + + + +
    > grep hello
    +hello grep
    +
    + +
    + + + +
    > ls -a -l -h
    +drwxr-xr-x  4 mark 136B Oct 3 16:29 .
    +drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
    +-rw-r--r--  1 mark 1.3K Oct 3 16:28 spawning-processes.go
    +
    + +
    +

    Следующий пример: Исполняющие процессы (Exec'ing Processes). @@ -34,7 +260,7 @@

    diff --git a/public/stateful-goroutines b/public/stateful-goroutines index df7ce5a..66ba49a 100644 --- a/public/stateful-goroutines +++ b/public/stateful-goroutines @@ -23,6 +23,293 @@

    Go в примерах: Управление состоянием горутин (Stateful Goroutines)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    В предыдущем примере мы использовали явную блокировку +с мьютексами для синхронизации доступа к +общему состоянию между несколькими горутинами. Другой +вариант - использовать встроенные функции синхронизации +для горутин и каналов для достижения того же результата. +Этот подход, основанный на каналах, согласуется с идеями +Go о совместном использовании памяти путем обмена +данными и владения каждой частью данных ровно 1 горутиной.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "math/rand"
    +    "sync/atomic"
    +    "time"
    +)
    +
    + +
    +

    В этом примере наше состояние будет принадлежать +единственной горутине. Это гарантирует, что данные +никогда не будут повреждены при одновременном доступе. +Чтобы прочитать или записать это состояние, другие +горутины будут отправлять сообщения горутин-владельцу +и получать соответствующие ответы. Эти структуры +readOp и writeOp инкапсулируют эти запросы и +способ, которым владеет горутина-ответчик. +In this example our state will be owned by a single +goroutine. This will guarantee that the data is never +corrupted with concurrent access. In order to read or +write that state, other goroutines will send messages +to the owning goroutine and receive corresponding +replies. These readOp and writeOp structs +encapsulate those requests and a way for the owning +goroutine to respond.

    + +
    + +
    type readOp struct {
    +    key  int
    +    resp chan int
    +}
    +type writeOp struct {
    +    key  int
    +    val  int
    +    resp chan bool
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Как и прежде, мы посчитаем, сколько операций мы +выполняем.

    + +
    + +
        var readOps uint64
    +    var writeOps uint64
    +
    + +
    +

    Каналы чтения и записи будут использоваться +другими горутинами для выдачи запросов на чтение +и запись соответственно.

    + +
    + +
        reads := make(chan readOp)
    +    writes := make(chan writeOp)
    +
    + +
    +

    Эта горутина, которой принадлежит состояние, она же +является картой, как в предыдущем примере, но теперь +является частной для горутины с сохранением состояния. +Она постоянно выбирает каналы чтения и записи, +отвечая на запросы по мере их поступления. Ответ +выполняется, сначала выполняя запрошенную операцию, +а затем отправляя значение по каналу resp, +соответственно, чтобы указать успешность (и +ребуемое значение в случае reads).

    + +
    + +
        go func() {
    +        var state = make(map[int]int)
    +        for {
    +            select {
    +            case read := <-reads:
    +                read.resp <- state[read.key]
    +            case write := <-writes:
    +                state[write.key] = write.val
    +                write.resp <- true
    +            }
    +        }
    +    }()
    +
    + +
    +

    Запускаем 100 горутин для выдачи операций чтения +в горутину владеющую состоянием, через канал reads. +Каждое чтение требует создания readOp, отправки +его по каналу reads и получения результата по +resp каналу.

    + +
    + +
        for r := 0; r < 100; r++ {
    +        go func() {
    +            for {
    +                read := readOp{
    +                    key:  rand.Intn(5),
    +                    resp: make(chan int)}
    +                reads <- read
    +                <-read.resp
    +                atomic.AddUint64(&readOps, 1)
    +                time.Sleep(time.Millisecond)
    +            }
    +        }()
    +    }
    +
    + +
    +

    Так же делаем 10 записей.

    + +
    + +
        for w := 0; w < 10; w++ {
    +        go func() {
    +            for {
    +                write := writeOp{
    +                    key:  rand.Intn(5),
    +                    val:  rand.Intn(100),
    +                    resp: make(chan bool)}
    +                writes <- write
    +                <-write.resp
    +                atomic.AddUint64(&writeOps, 1)
    +                time.Sleep(time.Millisecond)
    +            }
    +        }()
    +    }
    +
    + +
    +

    Дадим горутинам отработать 1 секунду

    + +
    + +
        time.Sleep(time.Second)
    +
    + +
    +

    Наконец, выводим данные счетчиков

    + +
    + +
        readOpsFinal := atomic.LoadUint64(&readOps)
    +    fmt.Println("readOps:", readOpsFinal)
    +    writeOpsFinal := atomic.LoadUint64(&writeOps)
    +    fmt.Println("writeOps:", writeOpsFinal)
    +}
    +
    + +
    + + + + + + + + + + + + + +
    +

    Запуск нашей программы показывает, что управление +состоянием на основе горутин завершает около +80 000 операций.

    + +
    + +
    $ go run stateful-goroutines.go
    +readOps: 71708
    +writeOps: 7177
    +
    + +
    +

    Для этого конкретного случая подход, основанный на +горутине, был немного более сложным, чем подход, +основанный на мьютексе. Это может быть полезно в +некоторых случаях, например, когда задействованы +другие каналы или при управлении несколькими такими +мьютексами могут возникать ошибки. Вы должны +использовать тот подход, который кажется вам наиболее +естественным, особенно в отношении понимания +правильности вашей программы.

    + +
    + + +
    +

    Следующий пример: Сортировка (Sorting). @@ -34,7 +321,7 @@

    diff --git a/public/string-formatting b/public/string-formatting index 3e8ab84..d4c397f 100644 --- a/public/string-formatting +++ b/public/string-formatting @@ -23,6 +23,427 @@

    Go в примерах: Форматирование строк (String Formatting)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go предлагает отличную поддержку форматирования строк +с помощью printf. Вот несколько примеров типичных +задач форматирования строк.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "os"
    +)
    +
    + +
    + + + +
    type point struct {
    +    x, y int
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Go предлагает несколько “глаголов” созданных для +форматирования общих Go значений. Например, +это выведет инстанс нашей point структуры.

    + +
    + +
        p := point{1, 2}
    +    fmt.Printf("%v\n", p)
    +
    + +
    +

    Если значение является структурой, запись %+v +выведет названия полей структуры.

    + +
    + +
        fmt.Printf("%+v\n", p)
    +
    + +
    +

    Вариант %#v печатает синтаксическое представление +Go, то есть фрагмент исходного кода, который будет +генерировать это значение.

    + +
    + +
        fmt.Printf("%#v\n", p)
    +
    + +
    +

    Для вывода типа значения, используйте %T.

    + +
    + +
        fmt.Printf("%T\n", p)
    +
    + +
    +

    Форматирование логических значений не вызывает затруднений.

    + +
    + +
        fmt.Printf("%t\n", true)
    +
    + +
    +

    Возможно большое количество опций для форматирования +целых чисел. Используйте %d для стандартного, +десятеричного вывода.

    + +
    + +
        fmt.Printf("%d\n", 123)
    +
    + +
    +

    Бинарный вывод

    + +
    + +
        fmt.Printf("%b\n", 14)
    +
    + +
    +

    Вывод символа, соответсвующего заданному числу.

    + +
    + +
        fmt.Printf("%c\n", 33)
    +
    + +
    +

    %x - шестнадцатиричное значение.

    + +
    + +
        fmt.Printf("%x\n", 456)
    +
    + +
    +

    Так же есть несколько вариантов форматирования +чисел с плавающей точкой. Стандартный вывод %f.

    + +
    + +
        fmt.Printf("%f\n", 78.9)
    +
    + +
    +

    %e и %E приводит числло с плавающей точкой +к экспоненциальному представлению.

    + +
    + +
        fmt.Printf("%e\n", 123400000.0)
    +    fmt.Printf("%E\n", 123400000.0)
    +
    + +
    +

    Для стандартного вывода строк используйте %s.

    + +
    + +
        fmt.Printf("%s\n", "\"string\"")
    +
    + +
    +

    Для двойных ковычек как в исходниках Go, используйте %q.

    + +
    + +
        fmt.Printf("%q\n", "\"string\"")
    +
    + +
    +

    Так же как и с целочисленными ранее, %x отображает +строку в виде шестнадцатеричного исчисления, с двумя +символами вывода за каждый байт ввода.

    + +
    + +
        fmt.Printf("%x\n", "hex this")
    +
    + +
    +

    Чтобы вывести ссылку на указатель, используйте %p.

    + +
    + +
        fmt.Printf("%p\n", &p)
    +
    + +
    +

    При форматировании чисел вам часто захочется +контролировать ширину и точность получаемого значения. +Чтобы указать ширину целого числа, используйте +число после %. По-умолчанию результат будет +выровнен по правому краю и дополнен пробелами.

    + +
    + +
        fmt.Printf("|%6d|%6d|\n", 12, 345)
    +
    + +
    +

    Вы также можете указать ширину чисел с плавающей точкой, +также вы можете ограничить десятичную точность +одновременно с помощью синтаксиса ширина.точность.

    + +
    + +
        fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
    +
    + +
    +

    Для выравнивания по левому краю используйте флаг -.

    + +
    + +
        fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
    +
    + +
    +

    Вы также можете контролировать ширину при форматировании +строк, особенно для обеспечения их выравнивания в табличном +выводе. Стандартное выравнивание по правому краю.

    + +
    + +
        fmt.Printf("|%6s|%6s|\n", "foo", "b")
    +
    + +
    +

    Для выравнивания по левому краю используйте флаг -.

    + +
    + +
        fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
    +
    + +
    +

    До сих пор мы видели Printf, который печатает +отформатированную строку в os.Stdout. Sprintf +форматирует и возвращает строку, нигде не печатая.

    + +
    + +
        s := fmt.Sprintf("a %s", "string")
    +    fmt.Println(s)
    +
    + +
    +

    Вы можете отформатировать+вывести в io.Writers, используя +Fprintf.

    + +
    + +
        fmt.Fprintf(os.Stderr, "an %s\n", "error")
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run string-formatting.go
    +{1 2}
    +{x:1 y:2}
    +main.point{x:1, y:2}
    +main.point
    +true
    +123
    +1110
    +!
    +1c8
    +78.900000
    +1.234000e+08
    +1.234000E+08
    +"string"
    +"\"string\""
    +6865782074686973
    +0x42135100
    +|    12|   345|
    +|  1.20|  3.45|
    +|1.20  |3.45  |
    +|   foo|     b|
    +|foo   |b     |
    +a string
    +an error
    +
    + +
    +

    Следующий пример: Регулярные выражения (Regular Expressions). @@ -34,7 +455,7 @@

    diff --git a/public/string-functions b/public/string-functions index 04ab05a..8516f53 100644 --- a/public/string-functions +++ b/public/string-functions @@ -23,6 +23,181 @@

    Go в примерах: Строковые функции (String Functions)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Стандартная библиотека пакета strings предоставляет +множество удобных функций для работы со строками. Вот +некоторые из них.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    s "strings"
    +)
    +
    + +
    +

    Создаем алиас для функции fmt.Println, т.к. далее мы будем +часто вызывать эту функцию.

    + +
    + +
    var p = fmt.Println
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Данные функции доступны в пакете strings. Обратите внимание, +что все эти функции из пакета, а не методы строковых объектов. +Это означает, что нам необходимо передать первым аргументом +функции, строку, над которой мы производим операцию. Вы можете +найти больше строковых функций в официальной документации +к пакету.

    + +
    + +
        p("Contains:  ", s.Contains("test", "es"))
    +    p("Count:     ", s.Count("test", "t"))
    +    p("HasPrefix: ", s.HasPrefix("test", "te"))
    +    p("HasSuffix: ", s.HasSuffix("test", "st"))
    +    p("Index:     ", s.Index("test", "e"))
    +    p("Join:      ", s.Join([]string{"a", "b"}, "-"))
    +    p("Repeat:    ", s.Repeat("a", 5))
    +    p("Replace:   ", s.Replace("foo", "o", "0", -1))
    +    p("Replace:   ", s.Replace("foo", "o", "0", 1))
    +    p("Split:     ", s.Split("a-b-c-d-e", "-"))
    +    p("ToLower:   ", s.ToLower("TEST"))
    +    p("ToUpper:   ", s.ToUpper("test"))
    +    p()
    +
    + +
    +

    Примеры ниже не относятся к пакету strings, но о них +стоит упомянуть - это механизмы для получения длины +строки и получение символа по индексу.

    + +
    + +
        p("Len: ", len("hello"))
    +    p("Char:", "hello"[1])
    +}
    +
    + +
    +

    Обратите внимание, что len и индексация выше работают на +уровне байтов. Go использует строки в кодировке UTF-8, так +что это часто полезно как есть. Если вы работаете с +потенциально многобайтовыми символами, вам нужно использовать +операции с кодировкой. Смотрите строки, байты, руны и символы +в Go для получения дополнительной +информации.

    + +
    + + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run string-functions.go
    +Contains:   true
    +Count:      2
    +HasPrefix:  true
    +HasSuffix:  true
    +Index:      1
    +Join:       a-b
    +Repeat:     aaaaa
    +Replace:    f00
    +Replace:    f0o
    +Split:      [a b c d e]
    +ToLower:    test
    +ToUpper:    TEST
    +
    + +
    + + + +
    Len:  5
    +Char: 101
    +
    + +
    +

    Следующий пример: Форматирование строк (String Formatting). @@ -34,7 +209,7 @@

    diff --git a/public/structs b/public/structs index fadbae1..800cbf0 100644 --- a/public/structs +++ b/public/structs @@ -23,6 +23,244 @@

    Go в примерах: Структуры (Structs)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Структуры в Go - это коллекции полей определенных +типов. Как правило, они используются для логической +группировки данных.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    +

    Структура person имеет два поля name и age.

    + +
    + +
    type person struct {
    +    name string
    +    age  int
    +}
    +
    + +
    +

    Функция NewPerson создает новую струкутуру person с +заданным именем.

    + +
    + +
    func NewPerson(name string) *person {
    +
    + +
    +

    Вы можете безопасно вернуть указатель на локальную +переменную, так как локальная переменная переживет +область действия функции.

    + +
    + +
        p := person{name: name}
    +    p.age = 42
    +    return &p
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Так создается новая структура

    + +
    + +
        fmt.Println(person{"Bob", 20})
    +
    + +
    +

    Вы можете задавать имена для корректного +присваивания значений при создании структуры

    + +
    + +
        fmt.Println(person{name: "Alice", age: 30})
    +
    + +
    +

    Пропущенные поля будут нулевыми.

    + +
    + +
        fmt.Println(person{name: "Fred"})
    +
    + +
    +

    Префикс & возвращает указатель на структуру.

    + +
    + +
        fmt.Println(&person{name: "Ann", age: 40})
    +
    + +
    +

    Можно инкапсулировать создание новой структуры +в функцию

    + +
    + +
        fmt.Println(NewPerson("Jon"))
    +
    + +
    +

    Доступ к полям структуры осуществляется через +точку.

    + +
    + +
        s := person{name: "Sean", age: 50}
    +    fmt.Println(s.name)
    +
    + +
    +

    Вы также можете использовать точки со +структурными указателями - указатели автоматически +разыменовываются.

    + +
    + +
        sp := &s
    +    fmt.Println(sp.age)
    +
    + +
    +

    Структуры мутабельны.

    + +
    + +
        sp.age = 51
    +    fmt.Println(sp.age)
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run structs.go
    +{Bob 20}
    +{Alice 30}
    +{Fred 0}
    +&{Ann 40}
    +Sean
    +50
    +51
    +&{Jon 42}
    +
    + +
    +

    Следующий пример: Методы (Methods). @@ -34,7 +272,7 @@

    diff --git a/public/temporary-files-and-directories b/public/temporary-files-and-directories index 0bb9089..0827cd6 100644 --- a/public/temporary-files-and-directories +++ b/public/temporary-files-and-directories @@ -23,6 +23,212 @@

    Go в примерах: Временные файлы и директории (Temporary Files and Directories)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Во время выполнения программы мы часто хотим создавать +данные, которые не нужны после выхода из программы. +Временные файлы и каталоги полезны для этой цели, +поскольку они не загрязняют файловую систему с +течением времени.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "io/ioutil"
    +    "os"
    +    "path/filepath"
    +)
    +
    + +
    + + + +
    func check(e error) {
    +    if e != nil {
    +        panic(e)
    +    }
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Простейший способ создания временного файла - это +вызов ioutil.TempFile. Он создаст и откроет +файл для чтения и записи. Мы использовали "" в +качестве первого аргумента, и поэтому ioutil.TempFile +создаст файл в директории по-умолчанию.

    + +
    + +
        f, err := ioutil.TempFile("", "sample")
    +    check(err)
    +
    + +
    +

    Показать имя временного файла. В ОС на основе Unix +каталог, вероятно, будет /tmp. Имя файла начинается +с префикса, заданного в качестве второго аргумента +ioutil.TempFile, а остальное выбирается автоматически, +чтобы параллельные вызовы всегда создавали разные +имена файлов.

    + +
    + +
        fmt.Println("Temp file name:", f.Name())
    +
    + +
    +

    Удалите файл после того, как мы закончим. Через +некоторое время ОС, скорее всего, сама очистит +временные файлы, но рекомендуется делать это явно.

    + +
    + +
        defer os.Remove(f.Name())
    +
    + +
    +

    Мы можем записать какую-то информацию в файл.

    + +
    + +
        _, err = f.Write([]byte{1, 2, 3, 4})
    +    check(err)
    +
    + +
    +

    Если мы намереваемся написать много временных файлов, +мы можем предпочесть создать временный каталог. Аргументы +ioutil.TempDir совпадают с аргументами TempFile, +но он возвращает имя каталога, а не открытый файл.

    + +
    + +
        dname, err := ioutil.TempDir("", "sampledir")
    +    fmt.Println("Temp dir name:", dname)
    +
    + +
    + + + +
        defer os.RemoveAll(dname)
    +
    + +
    +

    Теперь мы можем синтезировать временные имена +файлов, добавив к ним префикс нашего +временного каталога.

    + +
    + +
        fname := filepath.Join(dname, "file1")
    +    err = ioutil.WriteFile(fname, []byte{1, 2}, 0666)
    +    check(err)
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run temporary-files-and-directories.go
    +Temp file name: /tmp/sample610887201
    +Temp dir name: /tmp/sampledir898854668
    +
    + +
    +

    Следующий пример: Тестирование (Testing). @@ -34,7 +240,7 @@

    diff --git a/public/testing b/public/testing index ae3d56f..52be24a 100644 --- a/public/testing +++ b/public/testing @@ -23,6 +23,205 @@

    Go в примерах: Тестирование (Testing)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Unit тестирование является важной частью написания +правильных программ Go. Пакет тестирования testing +предоставляет инструменты, необходимые для +написания unit тестов, а команда go test запускает +тесты.

    + +
    + + +
    +

    Для наглядности этот код находится в main пакете, +но это может быть любой пакет. Тестовый код обычно +находится в том же пакете, что и код, который он тестирует.

    + +
    + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "testing"
    +)
    +
    + +
    +

    Мы будем тестировать эту простую реализацию +целочисленного минимума. Обычно код, который мы +тестируем, находится в исходном файле с именем +что-то вроде intutils.go, а тестовый файл для +него будет называться intutils_test.go.

    + +
    + +
    func IntMin(a, b int) int {
    +    if a < b {
    +        return a
    +    } else {
    +        return b
    +    }
    +}
    +
    + +
    +

    Тест создается путем написания функции с именем, +начинающимся с Test.

    + +
    + +
    func TestIntMinBasic(t *testing.T) {
    +    ans := IntMin(2, -2)
    +    if ans != -2 {
    +
    + +
    +

    t.Error* сообщит об ошибках теста, но продолжит +выполнение теста. t.Fail* сообщит об ошибках +теста и немедленно остановит тест.

    + +
    + +
            t.Errorf("IntMin(2, -2) = %d; want -2", ans)
    +    }
    +}
    +
    + +
    +

    Написание тестов может быть повторяющимся, поэтому +идиоматично использовать table-driven style, где тестовые +входы и ожидаемые выходы перечислены в таблице, а один +цикл проходит по ним и выполняет тестовую логику.

    + +
    + +
    func TestIntMinTableDriven(t *testing.T) {
    +    var tests = []struct {
    +        a, b int
    +        want int
    +    }{
    +        {0, 1, 0},
    +        {1, 0, 0},
    +        {2, -2, -2},
    +        {0, -1, -1},
    +        {-1, 0, -1},
    +    }
    +
    + +
    +

    t.Run позволяет запускать «подтесты», по одному +для каждой записи таблицы. Они показываются +отдельно при выполнении go test -v.

    + +
    + +
        for _, tt := range tests {
    +
    + +
    + + + +
            testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
    +        t.Run(testname, func(t *testing.T) {
    +            ans := IntMin(tt.a, tt.b)
    +            if ans != tt.want {
    +                t.Errorf("got %d, want %d", ans, tt.want)
    +            }
    +        })
    +    }
    +}
    +
    + +
    + + + + + + + + +
    +

    Run all tests in the current project in verbose mode.

    + +
    + +
    $ go test -v
    +== RUN   TestIntMinBasic
    +--- PASS: TestIntMinBasic (0.00s)
    +=== RUN   TestIntMinTableDriven
    +=== RUN   TestIntMinTableDriven/0,1
    +=== RUN   TestIntMinTableDriven/1,0
    +=== RUN   TestIntMinTableDriven/2,-2
    +=== RUN   TestIntMinTableDriven/0,-1
    +=== RUN   TestIntMinTableDriven/-1,0
    +--- PASS: TestIntMinTableDriven (0.00s)
    +    --- PASS: TestIntMinTableDriven/0,1 (0.00s)
    +    --- PASS: TestIntMinTableDriven/1,0 (0.00s)
    +    --- PASS: TestIntMinTableDriven/2,-2 (0.00s)
    +    --- PASS: TestIntMinTableDriven/0,-1 (0.00s)
    +    --- PASS: TestIntMinTableDriven/-1,0 (0.00s)
    +PASS
    +ok      examples/testing    0.023s
    +
    + +
    +

    Следующий пример: Аргументы командной строки (Command-Line Arguments). @@ -34,7 +233,7 @@

    diff --git a/public/tickers b/public/tickers index e33731e..0d4ddd4 100644 --- a/public/tickers +++ b/public/tickers @@ -23,6 +23,145 @@

    Go в примерах: Тикеры (повторения) (Tickers)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Таймеры необходимы, когда надо выполнить +что-то в будущем один раз, а тикеры позволяют +повторять действия через определенные интервалы. Вот +пример того, как тикер выводит сообщение Tick at... +через заданный период, пока мы не остановим его.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Тикеры используют тот же механизм, что и таймеры: +канал, в который посылаются значения. Здесь мы +будем использовать range для чтения данных из +канала, которые будут поступать в него каждые +500мс.

    + +
    + +
        ticker := time.NewTicker(500 * time.Millisecond)
    +    done := make(chan bool)
    +
    + +
    + + + +
        go func() {
    +        for {
    +            select {
    +            case <-done:
    +                return
    +            case t := <-ticker.C:
    +                fmt.Println("Tick at", t)
    +            }
    +        }
    +    }()
    +
    + +
    +

    Тикеры могут быть остановлены так же как и таймеры. +Когда тикер будет остановлен, он не сможет больше +принимать значения в свой канал. Мы остановим его +через 1600мс.

    + +
    + +
        time.Sleep(1600 * time.Millisecond)
    +    ticker.Stop()
    +    done <- true
    +    fmt.Println("Ticker stopped")
    +}
    +
    + +
    + + + + + + + + +
    +

    Когда мы запускаем эту программу, тикер должен +выполнится 3 раза, после чего остановиться.

    + +
    + +
    $ go run tickers.go
    +Tick at 2012-09-23 11:29:56.487625 -0700 PDT
    +Tick at 2012-09-23 11:29:56.988063 -0700 PDT
    +Tick at 2012-09-23 11:29:57.488076 -0700 PDT
    +Ticker stopped
    +
    + +
    +

    Следующий пример: Пулы воркеров (Worker Pools). @@ -34,7 +173,7 @@

    diff --git a/public/time b/public/time index 75f4c6d..607b336 100644 --- a/public/time +++ b/public/time @@ -23,6 +23,239 @@

    Go в примерах: Время (Time)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go предлагает обширную поддержку для времени и +продолжительности; вот несколько примеров.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +    p := fmt.Println
    +
    + +
    +

    Начнем с получения текущего времени

    + +
    + +
        now := time.Now()
    +    p(now)
    +
    + +
    +

    Вы можете построить структуру time, указав год, +месяц, день и т.д. Время всегда связано с местоположением, +т.е. часовым поясом.

    + +
    + +
        then := time.Date(
    +        2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
    +    p(then)
    +
    + +
    +

    Вы можете извлечь различные компоненты значения времени.

    + +
    + +
        p(then.Year())
    +    p(then.Month())
    +    p(then.Day())
    +    p(then.Hour())
    +    p(then.Minute())
    +    p(then.Second())
    +    p(then.Nanosecond())
    +    p(then.Location())
    +
    + +
    +

    Получения дня недели доступно через метод Weekday.

    + +
    + +
        p(then.Weekday())
    +
    + +
    +

    Эти методы сравниваются два раза, проверяя, +происходит ли первый случай до, после или +одновременно со вторым, соответственно.

    + +
    + +
        p(then.Before(now))
    +    p(then.After(now))
    +    p(then.Equal(now))
    +
    + +
    +

    Метод Sub возвращает Duration, интервал между +двумя временами.

    + +
    + +
        diff := now.Sub(then)
    +    p(diff)
    +
    + +
    +

    Мы можем вычислить продолжительность.

    + +
    + +
        p(diff.Hours())
    +    p(diff.Minutes())
    +    p(diff.Seconds())
    +    p(diff.Nanoseconds())
    +
    + +
    +

    Вы можете использовать Add, чтобы продвинуть +время на заданную продолжительность, или с -, +чтобы переместиться назад.

    + +
    + +
        p(then.Add(diff))
    +    p(then.Add(-diff))
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run time.go
    +2012-10-31 15:50:13.793654 +0000 UTC
    +2009-11-17 20:34:58.651387237 +0000 UTC
    +2009
    +November
    +17
    +20
    +34
    +58
    +651387237
    +UTC
    +Tuesday
    +true
    +false
    +false
    +25891h15m15.142266763s
    +25891.25420618521
    +1.5534752523711128e+06
    +9.320851514226677e+07
    +93208515142266763
    +2012-10-31 15:50:13.793654 +0000 UTC
    +2006-12-05 01:19:43.509120474 +0000 UTC
    +
    + +
    +

    Далее мы рассмотрим время относительно эпохи Unix.

    + +
    + + +
    +

    Следующий пример: Epoch. @@ -34,7 +267,7 @@

    diff --git a/public/time-formatting-parsing b/public/time-formatting-parsing index 176c27a..b267f39 100644 --- a/public/time-formatting-parsing +++ b/public/time-formatting-parsing @@ -23,6 +23,173 @@

    Go в примерах: Форматирование времени (Time Formatting / Parsing)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Go supports time formatting and parsing via +pattern-based layouts.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +    p := fmt.Println
    +
    + +
    +

    Вот пример форматирования времени в соответствии с +RFC3339 с использованием соответствующей константы.

    + +
    + +
        t := time.Now()
    +    p(t.Format(time.RFC3339))
    +
    + +
    +

    Парсинг значений использует тот же синтаксис, что и Format.

    + +
    + +
        t1, e := time.Parse(
    +        time.RFC3339,
    +        "2012-11-01T22:08:41+00:00")
    +    p(t1)
    +
    + +
    +

    Форматирование и парсинг используют аргументы на +основе примеров. Обычно вы используете константу из пакета +time, но вы также можете предоставить собственные шаблоны. +Они должны использовать время в формате Mon Jan 2 +15:04:05 MST 2006, чтобы вывести на экран. Время в примере +должно быть точно таким, как показано: 2006 год, 15 для часа, +понедельник для дня недели и т.д.

    + +
    + +
        p(t.Format("3:04PM"))
    +    p(t.Format("Mon Jan _2 15:04:05 2006"))
    +    p(t.Format("2006-01-02T15:04:05.999999-07:00"))
    +    form := "3 04 PM"
    +    t2, e := time.Parse(form, "8 41 PM")
    +    p(t2)
    +
    + +
    +

    Для числовых представлений вы также можете использовать +стандартное форматирование строки с методами времени.

    + +
    + +
        fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
    +        t.Year(), t.Month(), t.Day(),
    +        t.Hour(), t.Minute(), t.Second())
    +
    + +
    +

    Parse вернет ошибку. если не сможет распарсить данные.

    + +
    + +
        ansic := "Mon Jan _2 15:04:05 2006"
    +    _, e = time.Parse(ansic, "8:41PM")
    +    p(e)
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run time-formatting-parsing.go 
    +2014-04-15T18:00:15-07:00
    +2012-11-01 22:08:41 +0000 +0000
    +6:00PM
    +Tue Apr 15 18:00:15 2014
    +2014-04-15T18:00:15.161182-07:00
    +0000-01-01 20:41:00 +0000 UTC
    +2014-04-15T18:00:15-00:00
    +parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": ...
    +
    + +
    +

    Следующий пример: Случайные числа (Random Numbers). @@ -34,7 +201,7 @@

    diff --git a/public/timeouts b/public/timeouts index 0ab0267..d84da21 100644 --- a/public/timeouts +++ b/public/timeouts @@ -23,6 +23,154 @@

    Go в примерах: Тайм-ауты (Timeouts)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Тайм-ауты важны для программ, которые подключаются +к внешним ресурсам или которым необходимо ограничить +время выполнения. Тайм-ауты в Go реализуются легко +и элегантно благодаря каналам и select‘ам.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    В нашем примере предположим, что мы выполняем внешний +вызов, который возвращает результат на канале c1 +через 2с. Обратите внимание, что канал буферизован, +поэтому отправка в goroutine неблокирующая. Это +обычная схема предотвращения утечек горутин в случае, +если канал никогда не читается.ё

    + +
    + +
        c1 := make(chan string, 1)
    +    go func() {
    +        time.Sleep(2 * time.Second)
    +        c1 <- "result 1"
    +    }()
    +
    + +
    +

    Вот select, реализующий тайм-аут. res := <-c1 +ожидает результата, а <-Time.After ожидает +значения, которое будет отправлено после истечения +времени ожидания 1с. Поскольку select продолжает +работу с первым полученным запросом, мы возьмем +тайм-аут, если операция займет больше разрешенных 1с.

    + +
    + +
        select {
    +    case res := <-c1:
    +        fmt.Println(res)
    +    case <-time.After(1 * time.Second):
    +        fmt.Println("timeout 1")
    +    }
    +
    + +
    +

    Если мы допустим время ожидания более 3с, то +получение от c2 будет успешным, и мы распечатаем +результат.

    + +
    + +
        c2 := make(chan string, 1)
    +    go func() {
    +        time.Sleep(2 * time.Second)
    +        c2 <- "result 2"
    +    }()
    +    select {
    +    case res := <-c2:
    +        fmt.Println(res)
    +    case <-time.After(3 * time.Second):
    +        fmt.Println("timeout 2")
    +    }
    +}
    +
    + +
    + + + + + + + + +
    +

    Запуск этой программы показывает время ожидания +первой и второй успешной операции.

    + +
    + +
    $ go run timeouts.go 
    +timeout 1
    +result 2
    +
    + +
    +

    Следующий пример: Неблокируемые операции в каналах (Non-Blocking Channel Operations). @@ -34,7 +182,7 @@

    diff --git a/public/timers b/public/timers index 20484a9..3c17241 100644 --- a/public/timers +++ b/public/timers @@ -23,6 +23,141 @@

    Go в примерах: Таймеры (Timers)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Часто мы хотим выполнить код Go в какой-то момент в +будущем или повторно через определенный промежуток +времени. Встроенные функции таймера и тикера Go +облегчают обе эти задачи. Сначала мы рассмотрим +таймеры, а затем тикеры.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "time"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Таймеры позволяет выполнить одно событие в будущем. +Вы сообщаете таймеру, как долго вы хотите ждать, +и он предоставляет канал, который будет уведомлен +в это время. Этот таймер будет ждать 2 секунды.

    + +
    + +
        timer1 := time.NewTimer(2 * time.Second)
    +
    + +
    +

    <-timer1.C блокирует канал таймера C пока +не будет отправлено сообщение о том, что таймер истек

    + +
    + +
        <-timer1.C
    +    fmt.Println("Timer 1 expired")
    +
    + +
    +

    Если бы вы просто хотели подождать, вы могли бы +использовать time.Sleep. Одна из причин, по +которой таймер может быть полезен, заключается +в том, что вы можете отменить таймер до его +истечения, как в этом примере.

    + +
    + +
        timer2 := time.NewTimer(time.Second)
    +    go func() {
    +        <-timer2.C
    +        fmt.Println("Timer 2 expired")
    +    }()
    +    stop2 := timer2.Stop()
    +    if stop2 {
    +        fmt.Println("Timer 2 stopped")
    +    }
    +}
    +
    + +
    + + + + + + + + +
    +

    Первый таймер истекает через ~2с после запуска программы, +но второй должен быть остановлен до того, как он истечет.

    + +
    + +
    $ go run timers.go
    +Timer 1 expired
    +Timer 2 stopped
    +
    + +
    +

    Следующий пример: Тикеры (повторения) (Tickers). @@ -34,7 +169,7 @@

    diff --git a/public/url b/public/url deleted file mode 100644 index 6c42ddc..0000000 --- a/public/url +++ /dev/null @@ -1,41 +0,0 @@ - - - - - Go в примерах: Парсинг URL (URL Parsing) - - - - -
    -

    Go в примерах: Парсинг URL (URL Parsing)

    - - -

    - Следующий пример: Хеш SHA1 (SHA1 Hashes). -

    - - -
    - - - - diff --git a/public/url-parsing b/public/url-parsing new file mode 100644 index 0000000..c05f749 --- /dev/null +++ b/public/url-parsing @@ -0,0 +1,240 @@ + + + + + Go в примерах: Парсинг URL (URL Parsing) + + + + +
    +

    Go в примерах: Парсинг URL (URL Parsing)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    URL - это уникальный локатор ресурса. +Рассмотрим как парсить URL в Go.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "net"
    +    "net/url"
    +)
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Мы будем разбирать этот URL, который содержит схему, +аутентификационные данные, хост, порт, путь, параметры +и фрагмент запроса.

    + +
    + +
        s := "postgres://user:pass@host.com:5432/path?k=v#f"
    +
    + +
    +

    Парсим URL и убеждаемся, что нет ошибок.

    + +
    + +
        u, err := url.Parse(s)
    +    if err != nil {
    +        panic(err)
    +    }
    +
    + +
    +

    Получаем схему

    + +
    + +
        fmt.Println(u.Scheme)
    +
    + +
    +

    User содержит всю аутентификационную информацию; используйте +Username и Password если надо получить конкретное поле.

    + +
    + +
        fmt.Println(u.User)
    +    fmt.Println(u.User.Username())
    +    p, _ := u.User.Password()
    +    fmt.Println(p)
    +
    + +
    +

    Host содержит поля хост и порт, если они определены. +Воспользуйтесь SplitHostPort, чтобы разделить их.

    + +
    + +
        fmt.Println(u.Host)
    +    host, port, _ := net.SplitHostPort(u.Host)
    +    fmt.Println(host)
    +    fmt.Println(port)
    +
    + +
    +

    Так можно получить путь и фрагмент после #.

    + +
    + +
        fmt.Println(u.Path)
    +    fmt.Println(u.Fragment)
    +
    + +
    +

    Для получения параметров в строке вида k=v +используйте RawQuery. Вы так же можете разобрать +запрос в карту. Разобранный запрос в карту из строк +превращается в срез строк, так первый элемент будет +находиться по адресу [0].

    + +
    + +
        fmt.Println(u.RawQuery)
    +    m, _ := url.ParseQuery(u.RawQuery)
    +    fmt.Println(m)
    +    fmt.Println(m["k"][0])
    +}
    +
    + +
    + + + + + + + + +
    +

    Запуск нашей программы парсинга URL показывает все +различные фрагменты, которые мы извлекли.

    + +
    + +
    $ go run url-parsing.go 
    +postgres
    +user:pass
    +user
    +pass
    +host.com:5432
    +host.com
    +5432
    +/path
    +f
    +k=v
    +map[k:[v]]
    +v
    +
    + +
    + + +

    + Следующий пример: Хеш SHA1 (SHA1 Hashes). +

    + + +
    + + + + diff --git a/public/values b/public/values index 3abd5d6..9acf817 100644 --- a/public/values +++ b/public/values @@ -23,6 +23,124 @@

    Go в примерах: Типы данных (Values)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    В Go существуют различные типы значений: строки, +целые числа, числа с плавающей точкой, булевы и т.д. +Вот некоторые примеры

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Строки могут быть сложены с помощью символа +.

    + +
    + +
        fmt.Println("go" + "lang")
    +
    + +
    +

    Целый числа и числа с плавающей точкой.

    + +
    + +
        fmt.Println("1+1 =", 1+1)
    +    fmt.Println("7.0/3.0 =", 7.0/3.0)
    +
    + +
    +

    Логические значения с логическими операторами

    + +
    + +
        fmt.Println(true && false)
    +    fmt.Println(true || false)
    +    fmt.Println(!true)
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run values.go
    +golang
    +1+1 = 2
    +7.0/3.0 = 2.3333333333333335
    +false
    +true
    +false
    +
    + +
    +

    Следующий пример: Переменные (Variables). @@ -34,7 +152,7 @@

    diff --git a/public/variables b/public/variables index cacb411..dba89ad 100644 --- a/public/variables +++ b/public/variables @@ -23,6 +23,156 @@

    Go в примерах: Переменные (Variables)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    В Go, переменные объявляются явно и используются +компилятором, например, для проверки корректного +вызова функции (типы аргументов)

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    var объявляет 1 или более переменных

    + +
    + +
        var a = "initial"
    +    fmt.Println(a)
    +
    + +
    +

    Вы можете объявить несколько переменных за раз

    + +
    + +
        var b, c int = 1, 2
    +    fmt.Println(b, c)
    +
    + +
    +

    Go будет определять тип по инициализированной переменной.

    + +
    + +
        var d = true
    +    fmt.Println(d)
    +
    + +
    +

    Переменные, объявленные без соответствующей инициализации, +имеют нулевое значение. Например, нулевое значение +для int равно 0.

    + +
    + +
        var e int
    +    fmt.Println(e)
    +
    + +
    +

    В Go существует короткий пператор := для +объявления и инициализации переменной. +Например, var f string = "apple" в короткой записи +превратится в

    + +
    + +
        f := "apple"
    +    fmt.Println(f)
    +}
    +
    + +
    + + + + + + + + +
    + + + +
    $ go run variables.go
    +initial
    +1 2
    +true
    +0
    +apple
    +
    + +
    +

    Следующий пример: Константы (Constants). @@ -34,7 +184,7 @@

    diff --git a/public/variadic-functions b/public/variadic-functions index 53e6729..20f43c3 100644 --- a/public/variadic-functions +++ b/public/variadic-functions @@ -23,6 +23,142 @@

    Go в примерах: Функции с переменным числом аргументов (Variadic Functions)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Функции с переменным числом аргументов +могут быть вызваны с любым количество аргументов. +Пример такой функции - это fmt.Println.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import "fmt"
    +
    + +
    +

    Это функция, которая может принимать любое количество +аргументов целых чисел.

    + +
    + +
    func sum(nums ...int) {
    +    fmt.Print(nums, " ")
    +    total := 0
    +    for _, num := range nums {
    +        total += num
    +    }
    +    fmt.Println(total)
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Такие функции можно вызывать обычным способом.

    + +
    + +
        sum(1, 2)
    +    sum(1, 2, 3)
    +
    + +
    +

    Если у вас есть несколько аргументов в срезе, +вы можете применить его к функции с переменным +числом аргументов таким образом func(slice...).

    + +
    + +
        nums := []int{1, 2, 3, 4}
    +    sum(nums...)
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run variadic-functions.go 
    +[1 2] 3
    +[1 2 3] 6
    +[1 2 3 4] 10
    +
    + +
    +

    Другим ключевым аспектом функций в Go являются +замыкания, которые мы рассмотрим далее.

    + +
    + + +
    +

    Следующий пример: Замыкания (Closures). @@ -34,7 +170,7 @@

    diff --git a/public/worker-pools b/public/worker-pools index 052fe79..734dae3 100644 --- a/public/worker-pools +++ b/public/worker-pools @@ -23,6 +23,196 @@

    Go в примерах: Пулы воркеров (Worker Pools)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    В этом примере мы рассмотрим, как реализовать +пул воркеров с использованием каналов и +горутин.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "time"
    +)
    +
    + +
    +

    Это воркер, который мы будем запускать в нескольких +параллельных инстансах. Эти воркеры будут получать +задания через канал jobs и отсылать результаты +в results. Мы будем ожидать одну секунду для +каждого задания для имитации тяжелого запроса.

    + +
    + +
    func worker(id int, jobs <-chan int, results chan<- int) {
    +    for j := range jobs {
    +        fmt.Println("worker", id, "started  job", j)
    +        time.Sleep(time.Second)
    +        fmt.Println("worker", id, "finished job", j)
    +        results <- j * 2
    +    }
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    Чтобы использовать наш воркер пул, нам нужно +отправить им задание и получить результаты выполнения. +Для этого мы делаем 2 канала.

    + +
    + +
        jobs := make(chan int, 100)
    +    results := make(chan int, 100)
    +
    + +
    +

    Стартуем 3 воркера, первоначально заблокированных, +т.к. еще нет заданий.

    + +
    + +
        for w := 1; w <= 3; w++ {
    +        go worker(w, jobs, results)
    +    }
    +
    + +
    +

    Посылаем 5 заданий (jobs) и затем закрываем +канал, сообщая о том что все задания отправлены.

    + +
    + +
        for j := 1; j <= 5; j++ {
    +        jobs <- j
    +    }
    +    close(jobs)
    +
    + +
    +

    Наконец мы собираем все результаты. Это также +гарантирует, что горутины закончились. Альтернативный +способ ожидания нескольких процедур заключается +в использовании WaitGroup.

    + +
    + +
        for a := 1; a <= 5; a++ {
    +        <-results
    +    }
    +}
    +
    + +
    + + + + + + + + + + + + + +
    +

    Наша программа показывает 5 заданий, выполняемых +различными воркерами. Программа занимает всего около +2 секунд, несмотря на выполнение около 5 секунд общей +работы, потому что одновременно работают 3 воркера.

    + +
    + +
    $ time go run worker-pools.go 
    +worker 1 started  job 1
    +worker 2 started  job 2
    +worker 3 started  job 3
    +worker 1 finished job 1
    +worker 1 started  job 4
    +worker 2 finished job 2
    +worker 2 started  job 5
    +worker 3 finished job 3
    +worker 1 finished job 4
    +worker 2 finished job 5
    +
    + +
    + + + +
    real    0m2.358s
    +
    + +
    +

    Следующий пример: WaitGroups. @@ -34,7 +224,7 @@

    diff --git a/public/writing-files b/public/writing-files index fd622c1..18f0906 100644 --- a/public/writing-files +++ b/public/writing-files @@ -23,6 +23,263 @@

    Go в примерах: Запись файлов (Writing Files)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Запись файло в Go следует тем же шаблонам, что мы +видели ранее в главе “Чтение”.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "bufio"
    +    "fmt"
    +    "io/ioutil"
    +    "os"
    +)
    +
    + +
    + + + +
    func check(e error) {
    +    if e != nil {
    +        panic(e)
    +    }
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    В этом примере показано вот как записать строку +(или только байты) в файл.

    + +
    + +
        d1 := []byte("hello\ngo\n")
    +    err := ioutil.WriteFile("/tmp/dat1", d1, 0644)
    +    check(err)
    +
    + +
    +

    Для более детальной записи откройте файл для записи.

    + +
    + +
        f, err := os.Create("/tmp/dat2")
    +    check(err)
    +
    + +
    +

    Идиоматично откладывать закрытие с помощью defer‘a +сразу после открытия файла.

    + +
    + +
        defer f.Close()
    +
    + +
    +

    Вы можете записать срез байт, как и ожидается.

    + +
    + +
        d2 := []byte{115, 111, 109, 101, 10}
    +    n2, err := f.Write(d2)
    +    check(err)
    +    fmt.Printf("wrote %d bytes\n", n2)
    +
    + +
    +

    Запись строки WriteString так же доступна.

    + +
    + +
        n3, err := f.WriteString("writes\n")
    +    fmt.Printf("wrote %d bytes\n", n3)
    +
    + +
    +

    Выполните синхронизацию Sync для сброса записей +в стабильное хранилище.

    + +
    + +
        f.Sync()
    +
    + +
    +

    bufio предоставляет буферизованных писателей +в дополнение к буферизованным читателям, которые +мы видели ранее.

    + +
    + +
        w := bufio.NewWriter(f)
    +    n4, err := w.WriteString("buffered\n")
    +    fmt.Printf("wrote %d bytes\n", n4)
    +
    + +
    +

    Используйте Flush, чтобы убедиться, что все +буферизованные операции были применены к основному +модулю записи.

    + +
    + +
        w.Flush()
    +
    + +
    + + + +
    }
    +
    + +
    + + + + + + + + + + + + + + + + + + +
    +

    Пробуем запустить запись в файл.

    + +
    + +
    $ go run writing-files.go 
    +wrote 5 bytes
    +wrote 7 bytes
    +wrote 9 bytes
    +
    + +
    +

    Потом проверим, что контент появился в файлах.

    + +
    + +
    $ cat /tmp/dat1
    +hello
    +go
    +$ cat /tmp/dat2
    +some
    +writes
    +buffered
    +
    + +
    +

    Далее мы рассмотрим применение некоторых идей файлового +ввода-вывода, которые мы только что видели, к потокам +stdin и stdout.

    + +
    + + +
    +

    Следующий пример: Строковые фильтры (Line Filters). @@ -34,7 +291,7 @@

    diff --git a/tools/generate.go b/tools/generate.go index 93380d4..1c22ada 100644 --- a/tools/generate.go +++ b/tools/generate.go @@ -238,7 +238,7 @@ func parseAndRenderSegs(sourcePath string) ([]*Seg, string) { func parseExamples() []*Example { var exampleNames []string - //re := regexp.MustCompile(`(?m)[a-z0-9]{1}[a-z0-9\-]{1,}[a-z0-9]{1}`) + for _, line := range readLines("examples.txt") { if line != "" && !strings.HasPrefix(line, "#") { exampleNames = append(exampleNames, line) @@ -249,18 +249,24 @@ func parseExamples() []*Example { if verbose() { fmt.Printf("Processing %s [%d/%d]\n", exampleName, i+1, len(exampleNames)) } - example := Example{Name: exampleName} - exampleID := strings.ToLower(exampleName) + + exampleID := exampleName + + if strings.Contains(exampleName, "|") { + parts := strings.Split(exampleName, "|") + exampleName, exampleID = parts[0], parts[1] + } + + exampleID = strings.ToLower(exampleID) exampleID = strings.Replace(exampleID, " ", "-", -1) exampleID = strings.Replace(exampleID, "/", "-", -1) exampleID = strings.Replace(exampleID, "'", "", -1) exampleID = dashPat.ReplaceAllString(exampleID, "-") - //str := `хеш-sha1-(sha1-hashes)` - //fmt.Println(re.FindString(str)) + example := Example{Name: exampleName} example.ID = exampleID - //example.ID = re.FindString(exampleID) + example.Segs = make([][]*Seg, 0) sourcePaths := mustGlob("examples/" + exampleID + "/*") for _, sourcePath := range sourcePaths {