diff options
Diffstat (limited to 'ru-ru')
-rw-r--r-- | ru-ru/c-ru.html.markdown | 483 | ||||
-rw-r--r-- | ru-ru/clojure-ru.html.markdown | 427 | ||||
-rw-r--r-- | ru-ru/erlang-ru.html.markdown | 256 | ||||
-rw-r--r-- | ru-ru/go-ru.html.markdown | 336 | ||||
-rw-r--r-- | ru-ru/haskell-ru.html.markdown | 546 | ||||
-rw-r--r-- | ru-ru/julia-ru.html.markdown | 750 | ||||
-rw-r--r-- | ru-ru/objective-c-ru.html.markdown | 317 | ||||
-rw-r--r-- | ru-ru/php-ru.html.markdown | 663 | ||||
-rw-r--r-- | ru-ru/python-ru.html.markdown | 493 | ||||
-rw-r--r-- | ru-ru/ruby-ru.html.markdown | 468 |
10 files changed, 4739 insertions, 0 deletions
diff --git a/ru-ru/c-ru.html.markdown b/ru-ru/c-ru.html.markdown new file mode 100644 index 00000000..5988b159 --- /dev/null +++ b/ru-ru/c-ru.html.markdown @@ -0,0 +1,483 @@ +--- +language: c +filename: learnc-ru.c +contributors: + - ["Adam Bard", "http://adambard.com/"] + - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"] +translators: + - ["Evlogy Sutormin", "http://evlogii.com"] +lang: ru-ru +--- + +Что ж, Си всё ещё является лидером среди современных высокопроизводительных языков. + +Для большинствоа программистов, Си – это самый низкоуровневый язык на котором они когда-либо писали, +но этот язык даёт больше, чем просто повышение производительности. +Держите это руководство в памяти и вы сможете использовать Си максимально эффективно. + +```c +// Однострочный комментарий начинается с // - доступен только после С99. + +/* +Многострочный комментарий выглядит так. Работает начиная с С89. +*/ + +// Импорт файлов происходит с помощью **#include** +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +// Файлы <в угловых скобочках> будут подключаться из стандартной библиотеки. +// Свои файлы необходимо подключать с помощью "двойных кавычек". +#include "my_header.h" + +// Объявление функций должно происходить в .h файлах или вверху .c файла. +void function_1(); +void function_2(); + +// Точка входа в программу – это функция main. +int main() { + // для форматированного вывода в консоль используется printf + // %d – означает, что будем выводить целое число, \n переводит указатель вывода + // на новую строчку + printf("%d\n", 0); // => напечатает "0" + // Каждый оператор заканчивается точкой с запятой. + + /////////////////////////////////////// + // Типы + /////////////////////////////////////// + + // int обычно имеет длину 4 байта + int x_int = 0; + + // short обычно имеет длину 2 байта + short x_short = 0; + + // char гарантированно имеет длину 1 байта + char x_char = 0; + char y_char = 'y'; // Символьные литералы заключаются в кавычки '' + + // long как правило занимает от 4 до 8 байт + // long long занимает как минимум 64 бита + long x_long = 0; + long long x_long_long = 0; + + // float это 32-битное число с плавающей точкой + float x_float = 0.0; + + // double это 64-битное число с плавающей точкой + double x_double = 0.0; + + // Целые типы могут быть беззнаковыми. + unsigned short ux_short; + unsigned int ux_int; + unsigned long long ux_long_long; + + // sizeof(T) возвращает размер переменной типа Т в байтах. + // sizeof(obj) возвращает размер объекта obj в байтах. + printf("%zu\n", sizeof(int)); // => 4 (на большинстве машин int занимает 4 байта) + + // Если аргуметом sizeof будет выражение, то этот аргумент вычисляется + // ещё во время компиляции кода (кроме динамических массивов). + int a = 1; + // size_t это беззнаковый целый тип который использует как минимум 2 байта + // для записи размера объекта + size_t size = sizeof(a++); // a++ не выполнится + printf("sizeof(a++) = %zu, где a = %d\n", size, a); + // выведет строку "sizeof(a++) = 4, где a = 1" (на 32-битной архитектуре) + + // Можно задать размер массива при объявлении. + char my_char_array[20]; // Этот массив занимает 1 * 20 = 20 байт + int my_int_array[20]; // Этот массив занимает 4 * 20 = 80 байт (сумма 4-битных слов) + + // Можно обнулить массив при объявлении. + char my_array[20] = {0}; + + // Индексация массива происходит также как и в других Си-подобных языках. + my_array[0]; // => 0 + + // Массивы изменяемы. Это просто память как и другие переменные. + my_array[1] = 2; + printf("%d\n", my_array[1]); // => 2 + + // В C99 (а также опционально в C11), массив может быть объявлен динамически. + // Размер массива не обязательно должен быть рассчитан на этапе компиляции. + printf("Enter the array size: "); // спрашиваем юзера размер массива + char buf[0x100]; + fgets(buf, sizeof buf, stdin); + size_t size = strtoul(buf, NULL, 10); // strtoul парсит строку в беззнаковое целое + int var_length_array[size]; // объявление динамического массива + printf("sizeof array = %zu\n", sizeof var_length_array); + // Вывод программы (в зависимости от архитектуры) будет таким: + // > Enter the array size: 10 + // > sizeof array = 40 + + // Строка – это просто массив символов, оканчивающийся нулевым (NUL (0x00)) байтом + // представляемым в строке специальным символом '\0'. + // Нам не нужно вставлять нулевой байт в строковой литерал, + // компилятор всё сделает за нас. + char a_string[20] = "This is a string"; + printf("%s\n", a_string); // %s обозначает вывод строки + + printf("%d\n", a_string[16]); // => 0 + // байт #17 тоже равен 0 (а также 18, 19, и 20) + + // Если между одинарными кавычками есть символ – это символьный литерал, + // но это тип int, а не char (по историческим причинам). + + int cha = 'a'; // хорошо + char chb = 'a'; // тоже хорошо (подразумевается преобразование int в char) + + /////////////////////////////////////// + // Операторы + /////////////////////////////////////// + + // Можно использовать множественное объявление. + int i1 = 1, i2 = 2; + float f1 = 1.0, f2 = 2.0; + + // Арифметика обычная + i1 + i2; // => 3 + i2 - i1; // => 1 + i2 * i1; // => 2 + i1 / i2; // => 0 (0.5, но обрезается до 0) + + f1 / f2; // => 0.5, плюс-минус погрешность потому что, + // цифры с плавающей точкой вычисляются неточно! + + // Модуль + 11 % 3; // => 2 + + // Операции сравнения вам уже знакомы, но в Си нет булевого типа. + // Вместо него используется int. 0 это false, всё остальное это true. + // Операции сравнения всегда возвращают 1 или 0. + 3 == 2; // => 0 (false) + 3 != 2; // => 1 (true) + 3 > 2; // => 1 + 3 < 2; // => 0 + 2 <= 2; // => 1 + 2 >= 2; // => 1 + + // Си это не Питон – операции сравнения могут быть только парными. + int a = 1; + // ОШИБКА: + int between_0_and_2 = 0 < a < 2; + // Правильно: + int between_0_and_2 = 0 < a && a < 2; + + // Логика + !3; // => 0 (логическое НЕ) + !0; // => 1 + 1 && 1; // => 1 (логическое И) + 0 && 1; // => 0 + 0 || 1; // => 1 (лигическое ИЛИ) + 0 || 0; // => 0 + + // Битовые операторы + ~0x0F; // => 0xF0 (побитовое отрицание) + 0x0F & 0xF0; // => 0x00 (побитовое И) + 0x0F | 0xF0; // => 0xFF (побитовое ИЛИ) + 0x04 ^ 0x0F; // => 0x0B (исключающее ИЛИ (XOR)) + 0x01 << 1; // => 0x02 (побитовый сдвиг влево (на 1)) + 0x02 >> 1; // => 0x01 (побитовый сдвиг вправо (на 1)) + + // Будьте осторожны при сдвиге беззнакового int, эти операции не определены: + // - сдвиг в знаковый бит у целого числа (int a = 1 << 32) + // - сдвиг влево отрицательных чисел (int a = -1 << 2) + + /////////////////////////////////////// + // Структуры ветвления + /////////////////////////////////////// + + // Условный оператор + if (0) { + printf("I am never run\n"); + } else if (0) { + printf("I am also never run\n"); + } else { + printf("I print\n"); + } + + // Цикл с предусловием + int ii = 0; + while (ii < 10) { + printf("%d, ", ii++); // инкрементация происходит после того как + // знаечние ii передано ("postincrement") + } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + //Цикл с постусловием + int kk = 0; + do { + printf("%d, ", kk); + } while (++kk < 10); // инкрементация происходит перед тем как + // передаётся знаечние kk ("preincrement") + // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + // Цикл со счётчиком + int jj; + for (jj=0; jj < 10; jj++) { + printf("%d, ", jj); + } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + // Ветвление с множественным выбором + switch (some_integral_expression) { + case 0: // значения должны быть целыми константами (и могут быть выражениями) + do_stuff(); + break; // если не написать break; то управление будет передено следующему блоку + case 1: + do_something_else(); + break; + default: + // если не было совпадения, то выполняется блок default: + fputs("ошибка!\n", stderr); + exit(-1); + break; + } + + /////////////////////////////////////// + // Форматирование вывода + /////////////////////////////////////// + + // Каждое выражение в Си имеет тип, но вы можете привести один тип к другому, + // если хотите (с некоторыми искажениями). + + int x_hex = 0x01; // Вы можете назначать переменные с помощью шеснадцатеричного кода. + + // Приведение типов будет пытаться сохранять цифровые значения. + printf("%d\n", x_hex); // => Prints 1 + printf("%d\n", (short) x_hex); // => Prints 1 + printf("%d\n", (char) x_hex); // => Prints 1 + + // Типы могут переполняться без вызова предупреждения. + printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 if char is 8 bits long) + + // Для определения максимального значения типов `char`, `signed char` и `unisigned char`, + // соответственно используйте CHAR_MAX, SCHAR_MAX и UCHAR_MAX макросы из <limits.h> + + // Целые типы могут быть приведены к вещественным и наоборот. + printf("%f\n", (float)100); // %f formats a float + printf("%lf\n", (double)100); // %lf formats a double + printf("%d\n", (char)100.0); + + /////////////////////////////////////// + // Указатели + /////////////////////////////////////// + + // Указатель – это переменная которая хранит адрес в памяти. + // При объявлении указателя указывается тип данных переменной на которую он будет ссылаться. + // Вы можете получить адрес любой переменной, а потом работать с ним. + + // Используйте & для получения адреса переменной. + int x = 0; + printf("%p\n", (void *)&x); // => Напечатает адрес в памяти, где лежит переменная x + // (%p выводит указатель на void *) + + // Для объявления указателя нужно поставить * перед именем. + int *px, not_a_pointer; // px это указатель на int + px = &x; // сохранит адрес x в px + printf("%p\n", (void *)px); // => Напечатает адрес в памяти, где лежит переменная px + printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer)); + // => Напечатает "8, 4" в 64 битной системе + + // Для того, чтобы получить знаечние по адресу, напечатайте * перед именем. + // Да, использование * при объявлении указателя и получении значения по адресу + // немного запутано, но вы привыкнете. + printf("%d\n", *px); // => Напечаатет 0, значение перемененной x + + // Вы также можете изменять значение, на которое указывает указатель. + (*px)++; // Инкрементирует значение на которое указывает px на единицу + printf("%d\n", *px); // => Напечатает 1 + printf("%d\n", x); // => Напечатает 1 + + // Массивы удобно использовать для болшого количества однотипных данных. + int x_array[20]; + int xx; + for (xx = 0; xx < 20; xx++) { + x_array[xx] = 20 - xx; + } // Объявление x_array с значениями 20, 19, 18,... 2, 1 + + // Объявление указателя на int с адресом массива. + int* x_ptr = x_array; + // x_ptr сейчас указывает на первый элемент массива (со значением 20). + // Это рабоатет, потому что имя массива возвращает указатель на первый элемент. + // Например, когда массив передаётся в функцию или назначается указателю, он + // невявно преобразуется в указатель. + // Исключения: когда массив является аргументом для оператор '&': + int arr[10]; + int (*ptr_to_arr)[10] = &arr; // &arr не является 'int *'! + // он является "указателем на массив" (из десяти 'int'ов). + // или когда массив это строчный литерал, используемый при объявлении массива символов: + char arr[] = "foobarbazquirk"; + // или когда массив является аргументом `sizeof` или `alignof` операторов: + int arr[10]; + int *ptr = arr; // то же самое что и "int *ptr = &arr[0];" + printf("%zu %zu\n", sizeof arr, sizeof ptr); // напечатает "40, 4" или "40, 8" + + // Декрементация и инкрементация указателей зависит от их типа + // (это называется арифметика указателей) + printf("%d\n", *(x_ptr + 1)); // => Напечатает 19 + printf("%d\n", x_array[1]); // => Напечатает 19 + + // Вы также можете динамически выделять несколько блоков памяти с помощью + // функции malloc из стандартной библиотеки, которая принимает один + // аргумент типа size_t – количество байт необходимых для выделения. + int *my_ptr = malloc(sizeof(*my_ptr) * 20); + for (xx = 0; xx < 20; xx++) { + *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx + } // Выделяет память для 20, 19, 18, 17... 2, 1 (как int'ы) + + // Работа с памятью с помощью указателей может давать неожиданные и + // непредсказуемые результаты. + printf("%d\n", *(my_ptr + 21)); // => Напечатает кто-нибудь-знает-что? + // Скорей всего программа вылетит. + + // Когда вы закончили работать с памятью, которую ранее выделили, вам необходимо + // освободить её, иначе это может вызвать утечку памяти или ошибки. + free(my_ptr); + + // Строки это массивы символов, но обычно они представляются как + // указатели на символ (как указатели на первый элемент массива). + // Хорошей практикой считается использование `const char *' при объявлении + // строчного литерала. При таком подходе литерал не может быть изменён. + // (например "foo"[0] = 'a' вызовет ошибку!) + + const char *my_str = "This is my very own string literal"; + printf("%c\n", *my_str); // => 'T' + + // Это не работает, если строка является массивом + // (потенциально задаваемой с помощью строкового литерала) + // который находиться в перезаписываемой части памяти: + + char foo[] = "foo"; + foo[0] = 'a'; // это выполнится и строка теперь "aoo" + + void function_1() +} // конец функции main() + +/////////////////////////////////////// +// Функции +/////////////////////////////////////// + +// Синтаксис объявления функции: +// <возвращаемый тип> <имя функции>(аргументы) + +int add_two_ints(int x1, int x2) { + return x1 + x2; // Используйте return для возврата значения +} + +/* +Данные в функцию передаются "по значению", но никто не мешает +вам передавать в функцию указатели и менять данные по указателям. + +Например: инвертировать строку прямо в функции +*/ + +// void означает, что функция ничего не возвращает +void str_reverse(char *str_in) { + char tmp; + int ii = 0; + size_t len = strlen(str_in); // `strlen()` является частью стандартной библиотеки + for (ii = 0; ii < len / 2; ii++) { + tmp = str_in[ii]; + str_in[ii] = str_in[len - ii - 1]; // ii-тый символ с конца + str_in[len - ii - 1] = tmp; + } +} + +char c[] = "This is a test."; +str_reverse(c); +printf("%s\n", c); // => Выведет ".tset a si sihT" + +/////////////////////////////////////// +// Типы и структуры определяемые пользователем +/////////////////////////////////////// + +// typedef используется для задания стандартным типам своих названий +typedef int my_type; +my_type my_type_var = 0; + +// Структуры это просто коллекция данных, память выделяется последовательно, +// в том порядке в котором записаны данные. +struct rectangle { + int width; + int height; +}; + +// sizeof(struct rectangle) == sizeof(int) + sizeof(int) – не всегда верно +// из-за особенностей компиляции (необычное поведение при отступах)[1]. + +void function_1() { + struct rectangle my_rec; + + // Доступ к структурам через точку + my_rec.width = 10; + my_rec.height = 20; + + // Вы можете объявить указатель на структуру + struct rectangle *my_rec_ptr = &my_rec; + + // Можно доступаться к структуре и через указатель + (*my_rec_ptr).width = 30; + + // ... или ещё лучше: используйте оператор -> для лучшей читабельночти + my_rec_ptr->height = 10; // то же что и "(*my_rec_ptr).height = 10;" +} + +// Вы можете применить typedef к структуре, для удобства. +typedef struct rectangle rect; + +int area(rect r) { + return r.width * r.height; +} + +// Если вы имеете большую структуру, можно доступаться к ней "по указателю", +// чтобы избежать копирования всей структуры. +int area(const rect *r) { + return r->width * r->height; +} + +/////////////////////////////////////// +// Указатели на функции +/////////////////////////////////////// + +/* +Во время исполнения функции находятся по известным адресам в памяти. +Указатель на функцию может быть использован для непосредственного вызова функции. +Однако синтаксис может сбивать с толку. + +Пример: использование str_reverse по указателю +*/ + +void str_reverse_through_pointer(char *str_in) { + // Определение функции через указатель. + void (*f)(char *); // Сигнатура должна полность совпадать с целевой функцией. + f = &str_reverse; // Присвоить фактический адрес (во время исполнения) + // "f = str_reverse;" тоже будет работать. + //Имя функции (как и массива) возвращает указатель на начало. + (*f)(str_in); // Просто вызываем функцию через указатель. + // "f(str_in);" или вот так +} +``` + +## На почитать + +Лучше всего найдите копию [K&R, aka "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language) +Это **книга** написанная создателями Си. Но будьте осторожны, она содержит идеи которые больше не считаются хорошими. + +Другой хороший ресурс: [Learn C the hard way](http://c.learncodethehardway.org/book/). + +Если у вас появился вопрос, почитайте [compl.lang.c Frequently Asked Questions](http://c-faq.com). + +Очень важно использовать правильные отступы и ставить пробелы в нужных местах. +Читаемый код лучше чем красивый или быстрый код. +Чтобы научиться писать хороший код, почитайте [Linux kernel coding stlye](https://www.kernel.org/doc/Documentation/CodingStyle). + +Также не забывайте, что [Google](http://google.com) и [Яндекс](http://yandex.ru) – ваши хорошие друзья. + +[1] http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member diff --git a/ru-ru/clojure-ru.html.markdown b/ru-ru/clojure-ru.html.markdown new file mode 100644 index 00000000..2f508a00 --- /dev/null +++ b/ru-ru/clojure-ru.html.markdown @@ -0,0 +1,427 @@ +--- +language: clojure +filename: learnclojure-ru.clj +contributors: + - ["Adam Bard", "http://adambard.com/"] +translators: + - ["Alexey Pirogov", "http://twitter.com/alex_pir"] +lang: ru-ru +--- + +Clojure, это представитель семейства Lisp-подобных языков, разработанный +для Java Virtual Machine. Язык идейно гораздо ближе к чистому +[функциональному программированию](https://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5) чем его прародитель Common Lisp, но в то же время обладает набором инструментов для работы с состоянием, +таких как [STM](https://ru.wikipedia.org/wiki/Software_transactional_memory). + +Благодаря такому сочетанию технологий в одном языке, разработка программ, +предполагающих конкурентное выполнение, значительно упрощается +и даже может быть автоматизирована. + +(Последующие примеры кода предполагают выполнение в Clojure версии 1.2 и выше) + + +```clojure +; Комментарии начинаются символом ";". + +; Код на языке Clojure записывается в виде "форм", +; которые представляют собой обычные списки элементов, разделенных пробелами, +; заключённые в круглые скобки +; +; Clojure Reader (инструмент языка, отвечающий за чтение исходного кода), +; анализируя форму, предполагает, что первым элементом формы (т.е. списка) +; является функция или макрос, который следует вызвать, передав ему +; в качестве аргументов остальные элементы списка-формы. + +; Первым вызовом в файле должен быть вызов функции ns, +; которая отвечает за выбор текущего пространства имен (namespace) +(ns learnclojure-ru) + +; Несколько простых примеров: + +; str объединяет в единую строку все свои аргументы +(str "Hello" " " "World") ; => "Hello World" + +; Арифметика тоже выглядит несложно +(+ 1 1) ; => 2 +(- 2 1) ; => 1 +(* 1 2) ; => 2 +(/ 2 1) ; => 2 + +; Проверка на равенство (Equality) +(= 1 1) ; => true +(= 2 1) ; => false + +; Для булевой логики вам может понадобиться not +(not true) ; => false + +; Вложенные формы, конечно же, допустимы и работают вполне предсказуемо +(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2 + +; Типы +;;;;;;;;;;;;; + +; Clojure использует типы Java для представления булевых значений, +; строк и чисел +; Узнать тип мы можем, использую функцию `class +(class 1) ; Целочисленные литералы типа по-умолчанию являются java.lang.Long +(class 1.) ; Числа с плавающей точкой, это java.lang.Double +(class "") ; Строки всегда заключаются в двойные кавычки + ; и представляют собой java.lang.String +(class false) ; Булевы значения, это экземпляры java.lang.Boolean +(class nil) ; "Пустое" значение называется "nil" + +; Если Вы захотите создать список из чисел, вы можете просто +; предварить форму списка символом "'", который подскажет Reader`у, +; что эта форма не требует вычисления +'(+ 1 2) ; => (+ 1 2) +; ("'", это краткая запись формы (quote (+ 1 2)) + +; "Квотированный" список можно вычислить, передав его функции eval +(eval '(+ 1 2)) ; => 3 + +; Коллекции и Последовательности +;;;;;;;;;;;;;;;;;;; + +; Списки (Lists) в clojure структурно представляют собой "связанные списки", +; тогда как Векторы (Vectors), устроены как массивы. +; Векторы и Списки тоже являются классами Java! +(class [1 2 3]); => clojure.lang.PersistentVector +(class '(1 2 3)); => clojure.lang.PersistentList + +; Список может быть записан, как (1 2 3), но в этом случае +; он будет воспринят reader`ом, как вызов функции. +; Есть два способа этого избежать: +; '(1 2 3) - квотирование, +; (list 1 2 3) - явное конструирование списка с помощью функции list. + +; "Коллекции", это некие наборы данных +; И списки, и векторы являются коллекциями: +(coll? '(1 2 3)) ; => true +(coll? [1 2 3]) ; => true + +; "Последовательности" (seqs), это абстракция над наборами данных, +; элементы которых "упакованы" последовательно. +; Списки - последовательности, а вектора - нет. +(seq? '(1 2 3)) ; => true +(seq? [1 2 3]) ; => false + +; Любая seq предоставляет доступ только к началу последовательности данных, +; не предоставляя информацию о её длине. +; При этом последовательности могут быть и бесконечными, +; т.к. являются ленивыми и предоставляют данные только по требованию! +(range 4) ; => (0 1 2 3) +(range) ; => (0 1 2 3 4 ...) (бесконечная последовательность!) +(take 4 (range)) ; (0 1 2 3) + +; Добавить элемент в начало списка или вектора можно с помощью функции cons +(cons 4 [1 2 3]) ; => (4 1 2 3) +(cons 4 '(1 2 3)) ; => (4 1 2 3) + +; Функция conj добавляет элемент в коллекцию +; максимально эффективным для неё способом. +; Для списков эффективно добавление в начло, а для векторов - в конец. +(conj [1 2 3] 4) ; => [1 2 3 4] +(conj '(1 2 3) 4) ; => (4 1 2 3) + +; Функция concat объединяет несколько списков и векторов в единый список +(concat [1 2] '(3 4)) ; => (1 2 3 4) + +; Работать с коллекциями удобно с помощью функций filter и map +(map inc [1 2 3]) ; => (2 3 4) +(filter even? [1 2 3]) ; => (2) + +; reduce поможет "свернуть" коллекцию +(reduce + [1 2 3 4]) +; = (+ (+ (+ 1 2) 3) 4) +; => 10 + +; Вызывая reduce, мы можем указать начальное значение +(reduce conj [] '(3 2 1)) +; = (conj (conj (conj [] 3) 2) 1) +; => [3 2 1] + +; Функции +;;;;;;;;;;;;;;;;;;;;; + +; Функция создается специальной формой fn. +; "Тело"" функции может состоять из нескольких форм, +; но результатом вызова функции всегда будет результат вычисления +; последней из них. +(fn [] "Hello World") ; => fn + +; (Вызов функции требует "оборачивания" fn-формы в форму вызова) +((fn [] "Hello World")) ; => "Hello World" + +; Назначить значению имя можно специальной формой def +(def x 1) +x ; => 1 + +; Назначить имя можно любому значению, в т.ч. и функции: +(def hello-world (fn [] "Hello World")) +(hello-world) ; => "Hello World" + +; Поскольку именование функций - очень частая операция, +; clojure позволяет, сделать это проще: +(defn hello-world [] "Hello World") + +; Вектор [] в форме описания функции, следующий сразу за именем, +; описывает параметры функции: +(defn hello [name] + (str "Hello " name)) +(hello "Steve") ; => "Hello Steve" + +; Одна функция может иметь сразу несколько наборов аргументов: +(defn hello3 + ([] "Hello World") + ([name] (str "Hello " name))) +(hello3 "Jake") ; => "Hello Jake" +(hello3) ; => "Hello World" + +; Также функция может иметь набор аргументов переменной длины +(defn count-args [& args] ; args будет содержать seq аргументов + (str "You passed " (count args) " args: " args)) +(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)" + +; Можно комбинировать оба подхода задания аргументов +(defn hello-count [name & args] + (str "Hello " name ", you passed " (count args) " extra args")) +(hello-count "Finn" 1 2 3) +; => "Hello Finn, you passed 3 extra args" + +; Для создания анонимных функций есть специальный синтаксис: +; функциональные литералы +(def hello2 #(str "Hello " %1)) +(hello2 "Fanny") ; => "Hello Fanny" + +; такие функциональные литералы удобно использовать с map, filter и reduce +(map #(* 10 %1) [1 2 3 5]) ; => (10 20 30 50) +(filter #(> %1 3) [1 2 3 4 5 6 7]) ; => (4 5 6 7) +(reduce #(str %1 "," %2) [1 2 3 4]) ; => "1,2,3,4" + +; Отображения (Maps) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Hash maps и array maps имеют одинаковый интерфейс. +; Hash maps производят поиск по ключу быстрее, но не сохраняют порядок ключей +(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap +(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap + +; Array maps автоматически преобразуются в hash maps, +; как только разрастутся до определенного размера + +; Отображения могут использовать в качестве ключей любые хэшируемые значения, +; однако предпочтительными являются ключи, +; являющиеся "ключевыми словами" (keywords) +(class :a) ; => clojure.lang.Keyword + +(def stringmap {"a" 1, "b" 2, "c" 3}) +stringmap ; => {"a" 1, "b" 2, "c" 3} + +(def keymap {:a 1, :b 2, :c 3}) +keymap ; => {:a 1, :c 3, :b 2} + +; Предыдущий пример содержит запятые в коде, однако reader не использует их, +; при обработке литералов - запятые просто воспринимаются, +; как "пробельные символы" (whitespaces) + +; Отображение может выступать в роли функции, возвращающей значение по ключу +(stringmap "a") ; => 1 +(keymap :a) ; => 1 + +; При попытке получить отсутствующее значение, будет возвращён nil +(stringmap "d") ; => nil + +; Иногда бывает удобно указать конкретное значение по-умолчанию: +({:a 1 :b 2} :c "Oops!") ; => "Oops!" + +; Keywords тоже могут использоваться в роли функций! +(:b keymap) ; => 2 + +; Однако этот фокус не пройдёт со строками. +;("a" stringmap) +; => Exception: java.lang.String cannot be cast to clojure.lang.IFn + +; Добавить пару ключ-значение в отображение можно функцией assoc +(def newkeymap (assoc keymap :d 4)) +newkeymap ; => {:a 1, :b 2, :c 3, :d 4} + +; Но всегда следует помнить, что значения в Clojure - неизменяемые! +keymap ; => {:a 1, :b 2, :c 3} - оригинал не был затронут + +; dissoc позволяет исключить значение по ключу +(dissoc keymap :a :b) ; => {:c 3} + +; Множества (Sets) +;;;;;;;;;;;;;;;;;; + +(class #{1 2 3}) ; => clojure.lang.PersistentHashSet +(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3} + +; Добавляются элементы посредством conj +(conj #{1 2 3} 4) ; => #{1 2 3 4} + +; Исключаются - посредством disj +(disj #{1 2 3} 1) ; => #{2 3} + +; Вызов множества, как функции, позволяет проверить +; принадлежность элемента этому множеству: +(#{1 2 3} 1) ; => 1 +(#{1 2 3} 4) ; => nil + +; В пространстве имен clojure.sets +; содержится множество функций для работы с множествами + +; Полезные формы +;;;;;;;;;;;;;;;;; + +; Конструкции ветвления в clojure, это обычные макросы +; и подобны их собратьям в других языках: +(if false "a" "b") ; => "b" +(if false "a") ; => nil + +; Специальная форма let позволяет присвоить имена значениям локально. +; При этом все изменения будут видны только вложенным формам: +(def a 10) +(let [a 1 b 2] + (> a b)) ; => false + +; Несколько форм можно объединить в одну форму посредством do +; Значением do-формы будет значение последней формы из списка вложенных в неё: +(do + (print "Hello") + "World") ; => "World" (prints "Hello") + +; Множество макросов содержит внутри себя неявную do-форму. +; Пример - макрос определения функции: +(defn print-and-say-hello [name] + (print "Saying hello to " name) + (str "Hello " name)) +(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff") + +; Ещё один пример - let: +(let [name "Urkel"] + (print "Saying hello to " name) + (str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel") + +; Модули +;;;;;;;;; + +; Форма "use" позволяет добавить в текущее пространство имен +; все имена (вместе со значениями) из указанного модуля: +(use 'clojure.set) + +; Теперь нам доступны операции над множествами: +(intersection #{1 2 3} #{2 3 4}) ; => #{2 3} +(difference #{1 2 3} #{2 3 4}) ; => #{1} + +; use позволяет указать, какие конкретно имена +; должны быть импортированы из модуля: +(use '[clojure.set :only [intersection]]) + +; Также модуль может быть импортирован формой require +(require 'clojure.string) + +; После этого модуль становится доступе в текущем пространстве имен, +; а вызов его функций может быть осуществлен указанием полного имени функции: +(clojure.string/blank? "") ; => true + +; Импортируемому модулю можно назначить короткое имя: +(require '[clojure.string :as str]) +(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst." +; (Литерал вида #"" обозначает регулярное выражение) + +; Вместо отдельной формы require (и use, хотя это и не приветствуется) можно +; указать необходимые модули прямо в форме ns: +(ns test + (:require + [clojure.string :as str] ; Внимание: при указании внутри формы ns + [clojure.set :as set])) ; имена пакетов не квотируются! + +; Java +;;;;;;; + +; Стандартная библиотека Java очень богата, +; и всё это богатство доступно и для Clojure! + +; import позволяет импортировать модули Java +(import java.util.Date) + +; В том числе и из ns +(ns test + (:import java.util.Date + java.util.Calendar)) + +; Имя класса, сопровождаемое символом "." позволяет +; инстанцировать объекты Java-классов: +(Date.) ; <a date object> + +; форма . позволяет вызывать методы: +(. (Date.) getTime) ; <a timestamp> +(.getTime (Date.)) ; а можно и так + +; Статические методы вызываются как функции модуля: +(System/currentTimeMillis) ; <a timestamp> (Модуль system всегда доступен!) + +; doto позволяет удобно работать с объектами, изменяющими свое состояние +(import java.util.Calendar) +(doto (Calendar/getInstance) + (.set 2000 1 1 0 0 0) + .getTime) ; => A Date. set to 2000-01-01 00:00:00 + +; Работа с изменяемым сотоянием +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Clojure предоставляет набор инструментов +; для работы с изменяемым состоянием: Software Transactional Memory. +; Структуры STM представлены тремя типами: +; - атомы (atoms) +; - агенты (agents) +; - ссылки (references) + +; Самые простые хранители состояния - атомы: +(def my-atom (atom {})) ; {} - начальное состояние атома + +; Обновляется атом посредством swap!. +; swap! применяет функцию аргумент к текущему значению +; атома и помещает в атом результат +(swap! my-atom assoc :a 1) ; Обновляет my-atom, помещая в него (assoc {} :a 1) +(swap! my-atom assoc :b 2) ; Обновляет my-atom, помещая в него (assoc {:a 1} :b 2) + +; Получить значение атома можно посредством '@' +; (провести так называемую операцию dereference) +my-atom ;=> Atom<#...> (Возвращает объект типа Atom) +@my-atom ; => {:a 1 :b 2} + +; Пример реализации счётчика на атоме +(def counter (atom 0)) +(defn inc-counter [] + (swap! counter inc)) + +(inc-counter) +(inc-counter) +(inc-counter) +(inc-counter) +(inc-counter) + +@counter ; => 5 + +; С другими STM-конструкциями - refs и agents - можно ознакомиться тут: +; Refs: http://clojure.org/refs +; Agents: http://clojure.org/agents +``` + +### Для будущего чтения + +Это руководство не претендует на полноту, но мы смеем надеяться, способно вызвать интерес к дальнейшему изучению языка. + +Clojure.org - сайт содержит большое количество статей по языку: +[http://clojure.org/](http://clojure.org/) + +Clojuredocs.org - сайт документации языка с примерами использования функций: +[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core) + +4Clojure - отличный способ закрепить навыки программирования на clojure, решая задачи вместе с коллегами со всего мира: +[http://www.4clojure.com/](http://www.4clojure.com/) + +Clojure-doc.org (да, именно) неплохой перечень статей для начинающих: +[http://clojure-doc.org/](http://clojure-doc.org/) diff --git a/ru-ru/erlang-ru.html.markdown b/ru-ru/erlang-ru.html.markdown new file mode 100644 index 00000000..99ea79ee --- /dev/null +++ b/ru-ru/erlang-ru.html.markdown @@ -0,0 +1,256 @@ +--- +language: erlang +contributors: + - ["Giovanni Cappellotto", "http://www.focustheweb.com/"] +translators: + - ["Nikita Kalashnikov", "https://root.yuuzukiyo.net/"] +filename: learnerlang-ru.erl +lang: ru-ru +--- + +```erlang +% Символ процента предваряет однострочный комментарий. + +%% Два символа процента обычно используются для комментариев к функциям. + +%%% Три символа процента используются для комментариев к модулям. + +% Пунктуационные знаки, используемые в Erlang: +% Запятая (`,`) разделяет аргументы в вызовах функций, структурах данных и +% образцах. +% Точка (`.`) (с пробелом после них) разделяет функции и выражения в +% оболочке. +% Точка с запятой (`;`) разделяет выражения в следующих контекстах: +% формулы функций, выражения `case`, `if`, `try..catch` и `receive`. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 1. Переменные и сопоставление с образцом. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Num = 42. % Все названия переменных начинаются с большой буквы. + +% Erlang использует единичное присваивание переменным. Если вы попытаетесь +% присвоить другое значение переменной `Num`, вы получите ошибку. +Num = 43. % ** exception error: no match of right hand side value 43 + +% В большинстве языков `=` обозначает операцию присвоения. В отличие от них, в +% Erlang `=` — операция сопоставления с образцом. `Lhs = Rhs` на самом +% деле подразумевает «вычисли правую часть выражения (Rhs) и затем сопоставь +% результат с образцом слева (Lhs)». +Num = 7 * 6. + +% Числа с плавающей точкой. +Pi = 3.14159. + +% Атомы используются для представления различных нечисловых констант. Названия +% атомов начинаются с буквы в нижнем регистре, за которой могут следовать другие +% буквы английского алфавита, цифры, символ подчёркивания (`_`) или «собака» +% (`@`). +Hello = hello. +OtherNode = example@node. + +% Если в имени атома нужно использовать другие символы, кроме допустимых, +% имя атома необходимо взять в одинарные кавычки (`'`). +AtomWithSpace = 'some atom with space'. + +% Кортежы подобны структурам в языке C. +Point = {point, 10, 45}. + +% Если нужно извлечь определённые данные из кортежа, используется оператор +% сопоставления с образцом — `=`. +{point, X, Y} = Point. % X = 10, Y = 45 + +% Символ `_` может использоваться как «заполнитель» для переменных, значения +% которых в текущем выражении нас не интересуют. Он называется анонимной +% переменной. В отличие от остальных переменных, множественные использования +% `_` в одном образце не требуют, чтобы все значения, присваевыемые этой +% переменной, были идентичными. +Person = {person, {name, {first, joe}, {last, armstrong}}, {footsize, 42}}. +{_, {_, {_, Who}, _}, _} = Person. % Who = joe + +% Список создаётся путём заключения его элементов в квадратные скобки и +% разделения их запятыми. Отдельные элементы списка могут быть любого типа. +% Первый элемент списка называется головой списка. Список, получающийся в +% результате отделения головы, называется хвостом списка. +ThingsToBuy = [{apples, 10}, {pears, 6}, {milk, 3}]. + +% Если `T` — список, то `[H|T]` — тоже список, где `H` является головой, а `T` — +% хвостом. Вертикальная черта (`|`) разделяет голову и хвост списка. +% `[]` — пустой список. +% Мы можем извлекать элементы из списка с помощью сопоставления с образцом. +% Если у нас есть непустой список `L`, тогда выражение `[X|Y] = L`, где `X` и +% `Y` — свободные (не связанные с другими значениям) переменные, извлечёт голову +% списка в `X` и его хвост в `Y`. +[FirstThing|OtherThingsToBuy] = ThingsToBuy. +% FirstThing = {apples, 10} +% OtherThingsToBuy = {pears, 6}, {milk, 3} + +% В Erlang нет строк как отдельного типа. Все используемые в программах строки +% являются обычным списком целых чисел. Строковые значения всегда должны быть в +% двойных кавычках (`"`). +Name = "Hello". +[72, 101, 108, 108, 111] = "Hello". + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 2. Последовательное программирование. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Модуль — основная единица кода в Erlang. В них пишутся и сохраняются все +% функции. Модули хранятся в файлах с расширением `.erl`. +% Модули должны быть скомпилированы перед тем, как использовать код из них. +% Скомпилированный файл модуля имеет разрешение `.beam`. +-module(geometry). +-export([area/1]). % список функций, экспортируемых из модуля. + +% Функция `area` состоит из двух формул (clauses). Формулы отделяются друг от +% друга точкой с запятой, после последнего определения должна стоять точка с +% пробелом после неё. +% Каждое определение имеет заголовок и тело. Заголовок состоит из названия +% функции и образца (в скобках); тело состоит из последовательных выражений, +% вычисляемых, когда аргументы функции совпадают с образцом в заголовке. +% Сопоставление с образцами в заголовках происходит в том порядке, в котором +% они перечислены в определении функции. +area({rectangle, Width, Ht}) -> Width * Ht; +area({circle, R}) -> 3.14159 * R * R. + +% Компиляция файла с исходным кодом geometry.erl. +c(geometry). % {ok,geometry} + +% Необходимо указывать имя модуля вместе с именем функции для определения, какую +% именно фукнцию мы хотим вызвать. +geometry:area({rectangle, 10, 5}). % 50 +geometry:area({circle, 1.4}). % 6.15752 + +% В Erlang две функции с разной арностью (числом аргументов) в пределах одного +% модуля представляются как две разные функции. +-module(lib_misc). +-export([sum/1]). % экспорт функции `sum` с арностью 1, принимающую один аргумент. +sum(L) -> sum(L, 0). +sum([], N) -> N; +sum([H|T], N) -> sum(T, H+N). + +% Fun'ы — анонимные функции, называемые так по причине отсутствия имени. Зато +% их можно присваивать переменным. +Double = fun(X) -> 2*X end. % `Double` указывает на анонимную функцию с идентификатором: #Fun<erl_eval.6.17052888> +Double(2). % 4 + +% Функции могут принимать fun'ы как параметры и возвращать их в качестве +% результата вычислений. +Mult = fun(Times) -> ( fun(X) -> X * Times end ) end. +Triple = Mult(3). +Triple(5). % 15 + +% Выделения списоков (list comprehensions) — выражения, создающие списки без +% применения анонимных функций, фильтров или map'ов. +% Запись `[F(X) || X <- L]` значит «список `F(X)`, где `X` последовательно +% выбирается из списка `L`». +L = [1,2,3,4,5]. +[2*X || X <- L]. % [2,4,6,8,10] +% В выделениях списков могут быть генераторы и фильтры для отделения подмножеств +% генерируемых значений. +EvenNumbers = [N || N <- [1, 2, 3, 4], N rem 2 == 0]. % [2, 4] + +% Охранные выражения используются для простых проверок переменных в образцах, +% что значительно расширяет возможности сопоставления. Они могут использоваться +% в заголовках определений функций, предварённые ключевым словом `when`, а также +% в условных конструкциях. +max(X, Y) when X > Y -> X; +max(X, Y) -> Y. + +% Охранные выражения можно группировать, разделяя запятой. +% Последовательность `GuardExpr1, GuardExpr2, ..., GuardExprN` является истинной +% только в том случае, когда все выражения, которые она содержат, являются +% истинными. +is_cat(A) when is_atom(A), A =:= cat -> true; +is_cat(A) -> false. +is_dog(A) when is_atom(A), A =:= dog -> true; +is_dog(A) -> false. + +% Последовательность охранных выражений, разделённых точками с запятой, является +% истинной в том случае, если хотя бы одно выражение из списка `G1; G2; ...; Gn` +% является истинным. +is_pet(A) when is_dog(A); is_cat(A) -> true; +is_pet(A) -> false. + +% Записи предоставляют возможность именования определённых элементов в кортежах. +% Определения записей могут быть включены в исходный код модулей Erlang или же +% в заголовочные файлы с расширением `.hrl`. +-record(todo, { + status = reminder, % Значение по умолчанию. + who = joe, + text +}). + +% Для чтения определений записей из файлов в оболочке можно использовать команду +% `rr`. +rr("records.hrl"). % [todo] + +% Создание и изменение записей. +X = #todo{}. +% #todo{status = reminder, who = joe, text = undefined} +X1 = #todo{status = urgent, text = "Fix errata in book"}. +% #todo{status = urgent, who = joe, text = "Fix errata in book"} +X2 = X1#todo{status = done}. +% #todo{status = done,who = joe,text = "Fix errata in book"} + +% Условное выражение `case`. +% Функция `filter` возвращет список всех элементов `X` из списка `L`, для +% которых выражение `P(X)` является истинным. +filter(P, [H|T]) -> + case P(H) of + true -> [H|filter(P, T)]; + false -> filter(P, T) + end; +filter(P, []) -> []. +filter(fun(X) -> X rem 2 == 0 end, [1, 2, 3, 4]). % [2, 4] + +% Условное выражение `if`. +max(X, Y) -> + if + X > Y -> X; + X < Y -> Y; + true -> nil; + end. + +% Внимание: в выражении `if` должно быть как минимум одно охранное выраженние, +% вычисляющееся в true, иначе возникнет исключение. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 3. Обработка исключений. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Исключения возникают в случае внутренних ошибок системы или вызываются +% непосредственно из кода программы с помощью вызовов `throw(Exception)`, +% `exit(Exception)` или `erlang:error(Exception)`. +generate_exception(1) -> a; +generate_exception(2) -> throw(a); +generate_exception(3) -> exit(a); +generate_exception(4) -> {'EXIT', a}; +generate_exception(5) -> erlang:error(a). + +% В Erlang есть два способа обработки исключений. Первый заключается в +% использовании выражения `try..catch` в функции, в которой возможен выброс +% исключения. +catcher(N) -> + try generate_exception(N) of + Val -> {N, normal, Val} + catch + throw:X -> {N, caught, thrown, X}; + exit:X -> {N, caught, exited, X}; + error:X -> {N, caught, error, X} + end. + +% Второй способ заключается в использовании `catch`. Во время поимки исключения +% оно преобразуется в кортеж с информацией об ошибке. +catcher(N) -> catch generate_exception(N). + +``` + +## Ссылки: + +* ["Learn You Some Erlang for great good!"](http://learnyousomeerlang.com/) +* ["Programming Erlang: Software for a Concurrent World" by Joe Armstrong](http://pragprog.com/book/jaerlang/programming-erlang) +* [Erlang/OTP Reference Documentation](http://www.erlang.org/doc/) +* [Erlang - Programming Rules and Conventions](http://www.erlang.se/doc/programming_rules.shtml) diff --git a/ru-ru/go-ru.html.markdown b/ru-ru/go-ru.html.markdown new file mode 100644 index 00000000..ffda01b7 --- /dev/null +++ b/ru-ru/go-ru.html.markdown @@ -0,0 +1,336 @@ +--- +language: Go +filename: learngo-ru.go +contributors: + - ["Sonia Keys", "https://github.com/soniakeys"] + - ["Christopher Bess", "https://github.com/cbess"] + - ["Jesse Johnson", "https://github.com/holocronweaver"] + - ["Quint Guvernator", "https://github.com/qguv"] +translators: + - ["Artem Medeusheyev", "https://github.com/armed"] + - ["Valery Cherepanov", "https://github.com/qumeric"] +lang: ru-ru +--- + +Go - это язык общего назначения, целью которого является удобство, простота, +конкуррентность. Это не тренд в компьютерных науках, а новейший и быстрый +способ решать насущные проблемы. + +Концепции Go схожи с другими императивными статически типизированными языками. +Быстро компилируется и быстро исполняется, имеет легкие в понимании конструкции +для создания масштабируемых и многопоточных программ. + +Может похвастаться отличной стандартной библиотекой и большим комьюнити, полным +энтузиастов. + +```go +// Однострочный комментарий +/* Многострочный + комментарий */ + +// Ключевое слово package присутствует в начале каждого файла. +// Main это специальное имя, обозначающее исполняемый файл, нежели библиотеку. +package main + +// Import предназначен для указания зависимостей этого файла. +import ( + "fmt" // Пакет в стандартной библиотеке Go + "net/http" // Да, это веб-сервер! + "strconv" // Конвертирование типов в строки и обратно + m "math" // Импортировать math под локальным именем m. +) + +// Объявление функции. Main это специальная функция, служащая точкой входа для +// исполняемой программы. Нравится вам или нет, но Go использует фигурные +// скобки. +func main() { + // Println выводит строку в stdout. + // Данная функция находится в пакете fmt. + fmt.Println("Hello world!") + + // Вызов другой функции из текущего пакета. + beyondHello() +} + +// Функции содержат входные параметры в круглых скобках. +// Пустые скобки все равно обязательны, даже если параметров нет. +func beyondHello() { + var x int // Переменные должны быть объявлены до их использования. + x = 3 // Присвоение значения переменной. + // Краткое определение := позволяет объявить перменную с автоматической + // подстановкой типа из значения. + y := 4 + sum, prod := learnMultiple(x, y) // Функция возвращает два значения. + fmt.Println("sum:", sum, "prod:", prod) // Простой вывод. + learnTypes() // < y minutes, learn more! +} + +// Функция имеющая входные параметры и возврат нескольких значений. +func learnMultiple(x, y int) (sum, prod int) { + return x + y, x * y // Возврат двух значений. +} + +// Некотрые встроенные типы и литералы. +func learnTypes() { + // Краткое определение переменной говорит само за себя. + s := "Learn Go!" // Тип string. + + s2 := `"Чистый" строковой литерал +может содержать переносы строк` // Тоже тип данных string + + // Символ не из ASCII. Исходный код Go в кодировке UTF-8. + g := 'Σ' // тип rune, это алиас для типа uint32, содержит символ юникода. + + f := 3.14195 // float64, 64-х битное число с плавающей точкой (IEEE-754). + c := 3 + 4i // complex128, внутри себя содержит два float64. + + // Синтаксис var с инициализациями. + var u uint = 7 // Беззнаковое, но размер зависит от реализации, как и у int. + var pi float32 = 22. / 7 + + // Синтаксис приведения типа с кратким определением + n := byte('\n') // byte – это алиас для uint8. + + // Массивы имеют фиксированный размер на момент компиляции. + var a4 [4]int // массив из 4-х int, инициализирован нулями. + a3 := [...]int{3, 1, 5} // массив из 3-х int, ручная инициализация. + + // Слайсы (slices) имеют динамическую длину. И массивы, и слайсы имеют свои + // преимущества, но слайсы используются гораздо чаще. + s3 := []int{4, 5, 9} // Сравните с a3. Тут нет троеточия. + s4 := make([]int, 4) // Выделение памяти для слайса из 4-х int (нули). + var d2 [][]float64 // Только объявление, память не выделяется. + bs := []byte("a slice") // Синтаксис приведения типов. + + p, q := learnMemory() // Объявление p и q как указателей на int. + fmt.Println(*p, *q) // * извлекает указатель. Печатает два int-а. + + // Map, также как и словарь или хеш из некоторых других языков, является + // ассоциативным массивом с динамически изменяемым размером. + m := map[string]int{"three": 3, "four": 4} + m["one"] = 1 + + delete(m, "three") // Встроенная функция, удаляет элемент из map-а. + + // Неиспользуемые переменные в Go являются ошибкой. + // Нижнее подчеркивание позволяет игнорировать такие переменные. + _, _, _, _, _, _, _, _, _ = s2, g, f, u, pi, n, a3, s4, bs + // Вывод считается использованием переменной. + fmt.Println(s, c, a4, s3, d2, m) + + learnFlowControl() // Идем дальше. +} + +// У Go есть полноценный сборщик мусора. В нем есть указатели но нет арифметики +// указателей. Вы можете допустить ошибку с указателем на nil, но не с +// инкрементацией указателя. +func learnMemory() (p, q *int) { + // Именованные возвращаемые значения p и q являются указателями на int. + p = new(int) // Встроенная функция new выделяет память. + // Выделенный int проинициализирован нулем, p больше не содержит nil. + s := make([]int, 20) // Выделение единого блока памяти под 20 int-ов. + s[3] = 7 // Присвоить значение одному из них. + r := -2 // Определить еще одну локальную переменную. + return &s[3], &r // Амперсанд(&) обозначает получение адреса переменной. +} + +func expensiveComputation() float64 { + return m.Exp(10) +} + +func learnFlowControl() { + // If-ы всегда требуют наличине фигурных скобок, но не круглых. + if true { + fmt.Println("told ya") + } + // Форматирование кода стандартизировано утилитой "go fmt". + if false { + // Будущего нет. + } else { + // Жизнь прекрасна. + } + // Используйте switch вместо нескольких if-else. + x := 42.0 + switch x { + case 0: + case 1: + case 42: + // Case-ы в Go не "проваливаются" (неявный break). + case 43: + // Не выполнится. + } + // For, как и if не требует круглых скобок + // Переменные, объявленные в for и if являются локальными. + for x := 0; x < 3; x++ { // ++ – это операция. + fmt.Println("итерация", x) + } + // Здесь x == 42. + + // For – это единственный цикл в Go, но у него есть альтернативные формы. + for { // Бесконечный цикл. + break // Не такой уж и бесконечный. + continue // Не выполнится. + } + // Как и в for, := в if-е означает объявление и присвоение значения y, + // проверка y > x происходит после. + if y := expensiveComputation(); y > x { + x = y + } + // Функции являются замыканиями. + xBig := func() bool { + return x > 10000 // Ссылается на x, объявленый выше switch. + } + fmt.Println("xBig:", xBig()) // true (т.к. мы присвоили x = e^10). + x = 1.3e3 // Тут х == 1300 + fmt.Println("xBig:", xBig()) // Теперь false. + + // Метки, куда же без них, их все любят. + goto love +love: + + learnDefer() // Быстрый обзор важного ключевого слова. + learnInterfaces() // О! Интерфейсы, идем далее. +} + +func learnDefer() (ok bool) { + // Отложенные(deferred) выражения выполняются сразу перед тем, как функция + // возвратит значение. + defer fmt.Println("deferred statements execute in reverse (LIFO) order.") + defer fmt.Println("\nThis line is being printed first because") + // defer широко используется для закрытия файлов, чтобы закрывающая файл + // функция находилась близко к открывающей. + return true +} + +// Объявление Stringer как интерфейса с одним методом, String. +type Stringer interface { + String() string +} + +// Объявление pair как структуры с двумя полями x и y типа int. +type pair struct { + x, y int +} + +// Объявление метода для типа pair. Теперь pair реализует интерфейс Stringer. +func (p pair) String() string { // p в данном случае называют receiver-ом. + // Sprintf – еще одна функция из пакета fmt. + // Обращение к полям p через точку. + return fmt.Sprintf("(%d, %d)", p.x, p.y) +} + +func learnInterfaces() { + // Синтаксис с фигурными скобками это "литерал структуры". Он возвращает + // проинициализированную структуру, а оператор := присваивает её p. + p := pair{3, 4} + fmt.Println(p.String()) // Вызов метода String у переменной p типа pair. + var i Stringer // Объявление i как типа с интерфейсом Stringer. + i = p // Валидно, т.к. pair реализует Stringer. + // Вызов метода String у i типа Stringer. Вывод такой же, что и выше. + fmt.Println(i.String()) + + // Функции в пакете fmt сами всегда вызывают метод String у объектов для + // получения строкового представления о них. + fmt.Println(p) // Вывод такой же, что и выше. Println вызывает метод String. + fmt.Println(i) // Вывод такой же, что и выше. + + learnVariadicParams("Учиться", "учиться", "и еще раз учиться!") +} + +// Функции могут иметь варьируемое количество параметров. +func learnVariadicParams(myStrings ...interface{}) { + // Вывести все параметры с помощью итерации. + for _, param := range myStrings { + fmt.Println("param:", param) + } + + // Передать все варьируемые параметры. + fmt.Println("params:", fmt.Sprintln(myStrings...)) + + learnErrorHandling() +} + +func learnErrorHandling() { + // Идиома ", ok" служит для обозначения корректного срабатывания чего-либо. + m := map[int]string{3: "three", 4: "four"} + if x, ok := m[1]; !ok { // ok будет false, потому что 1 нет в map-е. + fmt.Println("тут никого нет") + } else { + fmt.Print(x) // x содержал бы значение, если бы 1 был в map-е. + } + // Идиома ", err" служит для обозначения была ли ошибка или нет. + if _, err := strconv.Atoi("non-int"); err != nil { // _ игнорирует значение + // выведет "strconv.ParseInt: parsing "non-int": invalid syntax" + fmt.Println(err) + } + // Мы еще обратимся к интерфейсам чуть позже, а пока... + learnConcurrency() +} + +// c – это тип данных channel (канал), объект для конкуррентного взаимодействия. +func inc(i int, c chan int) { + c <- i + 1 // когда channel слева, <- являтся оператором "отправки". +} + +// Будем использовать функцию inc для конкуррентной инкрементации чисел. +func learnConcurrency() { + // Тот же make, что и в случае со slice. Он предназначен для выделения + // памяти и инициализации типов slice, map и channel. + c := make(chan int) + // Старт трех конкуррентных goroutine. Числа будут инкрементированы + // конкуррентно и, может быть параллельно, если машина правильно + // сконфигурирована и позволяет это делать. Все они будут отправлены в один + // и тот же канал. + go inc(0, c) // go начинает новую горутину. + go inc(10, c) + go inc(-805, c) + // Считывание всех трех результатов из канала и вывод на экран. + // Нет никакой гарантии в каком порядке они будут выведены. + fmt.Println(<-c, <-c, <-c) // канал справа, <- обозначает "получение". + + cs := make(chan string) // другой канал, содержит строки. + cc := make(chan chan string) // канал каналов со строками. + go func() { c <- 84 }() // пуск новой горутины для отправки значения + go func() { cs <- "wordy" }() // еще раз, теперь для cs + // Select тоже что и switch, но работает с каналами. Он случайно выбирает + // готовый для взаимодействия канал. + select { + case i := <-c: // полученное значение можно присвоить переменной + fmt.Printf("это %T", i) + case <-cs: // либо значение можно игнорировать + fmt.Println("это строка") + case <-cc: // пустой канал, не готов для коммуникации. + fmt.Println("это не выполнится.") + } + // В этой точке значение будет получено из c или cs. Одна горутина будет + // завершена, другая останется заблокированной. + + learnWebProgramming() // Да, Go это может. +} + +// Всего одна функция из пакета http запускает web-сервер. +func learnWebProgramming() { + // У ListenAndServe первый параметр это TCP адрес, который нужно слушать. + // Второй параметр это интерфейс типа http.Handler. + err := http.ListenAndServe(":8080", pair{}) + fmt.Println(err) // не игнорируйте сообщения об ошибках +} + +// Реализация интерфейса http.Handler для pair, только один метод ServeHTTP. +func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Обработка запроса и отправка данных методом из http.ResponseWriter + w.Write([]byte("You learned Go in Y minutes!")) +} +``` + +## Что дальше + +Основа всех основ в Go это [официальный веб сайт](http://golang.org/). +Там можно пройти туториал, поиграться с интерактивной средой Go и почитать +объемную документацию. + +Для живого ознакомления рекомендуется почитать исходные коды [стандартной +библиотеки Go](http://golang.org/src/pkg/). Отлично задокументированная, она +является лучшим источником для чтения и понимания Go, его стиля и идиом. Либо +можно, кликнув на имени функции в [документации](http://golang.org/pkg/), +перейти к ее исходным кодам. diff --git a/ru-ru/haskell-ru.html.markdown b/ru-ru/haskell-ru.html.markdown new file mode 100644 index 00000000..03e66d05 --- /dev/null +++ b/ru-ru/haskell-ru.html.markdown @@ -0,0 +1,546 @@ +--- +language: haskell +contributors: + - ["Adit Bhargava", "http://adit.io"] +translators: + - ["Aleksey Pirogov", "http://astynax.github.io"] +lang: ru-ru +--- + +Haskell разрабатывался, как чистый функциональный язык программирования, применимый на практике. Язык известен благодаря своей системе типов, и "знаменит" благодаря монадам. [Меня][autor] же Haskell заставляет возвращаться к себе снова и снова именно своей элегантностью и [я][autor] получаю истинное удовольствие, программируя на Haskell. + +```haskell +-- Однострочные комментарии начинаются с двух дефисов +{- Многострочный комментарий +заключается в пару фигурных скобок с дефисами с внутренней стороны. +-} + +------------------------------------------------------- +-- 1. Примитивные типы и простейшие операции над ними +------------------------------------------------------- + +-- Числа объявляются просто +3 -- 3 + +-- Арифметика тоже выглядит вполне ожидаемо +1 + 1 -- 2 +8 - 1 -- 7 +10 * 2 -- 20 +35 / 5 -- 7.0 + +-- Операция деления всегда возвращает действительное число +35 / 4 -- 8.75 + +-- Делим нацело так +35 `div` 4 -- 8 + +-- Булевы значения - тоже примитивные значения +True +False + +-- Булева алгебра +not True -- False +not False -- True +1 == 1 -- True +1 /= 1 -- False +1 < 10 -- True + +-- В примере выше `not`, это функция, принимающая один аргумент. +-- При вызове функции в Haskell список аргументов +-- не нужно заключать в скобки - аргументы просто +-- перечисляются через пробелы сразу после имени функции. +-- Т.о. типичный вызов выглядит так: +-- func arg1 arg2 arg3... +-- Ниже же будет показано, как определять свои функции. + +-- Строки и символы +"Это строка." +'ы' -- а это символ +'Нельзя заключать длинные строки в одинарные кавычки.' -- ошибка! + +-- Строки можно конкатенировать +"Привет" ++ ", Мир!" -- "Привет, Мир!" + +-- При этом строки - это просто списки символов! +"Я - строка!" !! 0 -- 'Я' + + +---------------------------------------------------- +-- Списки и Кортежи +---------------------------------------------------- + +-- Все элементы списка в Haskell +-- должны иметь один и тот же тип. + +-- Эти два списка - эквивалентны: +[1, 2, 3, 4, 5] +[1..5] + +-- Haskell позволяет определять даже бесконечные списки! +[1..] -- список всех натуральных чисел! + +-- Бесконечные списки возможно в Haskell потому, что он "ленив". +-- В Haskell все вычисления производятся тогда и только тогда, +-- когда их результат потребуется. +-- Эта стратегия так и называется - "lazy evaluation". +-- Скажем, если вам нужен тысячный элемент из +-- списка натуральных чисел (бесконечного) и вы напишете так: + +[1..] !! 999 -- 1000 + +-- То Haskell вычислит элементы этого списка от 1 до 1000... +-- ... и остановится, ведь последующие элементы пока не нужны. +-- Это значит, что остальные элементы нашего +-- "бесконечного" списка не будут вычисляться! По крайней мере, +-- пока не понадобятся и они. + +-- Списки можно объединять +[1..5] ++ [6..10] + +-- И добавлять значения в начало +0:[1..5] -- [0, 1, 2, 3, 4, 5] + +-- А можно обратиться по индексу +[0..] !! 5 -- 5 + +-- Вот ещё несколько функций, часто используемых со списками +head [1..5] -- 1 +tail [1..5] -- [2, 3, 4, 5] +init [1..5] -- [1, 2, 3, 4] +last [1..5] -- 5 + +-- list comprehensions - "формулы" для описания списков +[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10] + +-- можно указать условие попадания элементов в список +[x*2 | x <- [1..5], x*2 > 4] -- [6, 8, 10] + +-- Списки могут даже состоять из других списков +[[1,2,3],[4,5,6]] !! 1 !! 2 -- 6 (вторая строка, третий столбец) + +-- Кортежи позволяют своим элементам иметь различные типы, +-- но при этом кортежи имеют фиксированную длину. +-- Кортеж: +("haskell", 1) + +-- Часто кортежи из двух элементов называются "парами". +-- Элементы пары можно получать так: +fst ("haskell", 1) -- "haskell" +snd ("haskell", 1) -- 1 + +---------------------------------------------------- +-- 3. Функции +---------------------------------------------------- +-- Простая функция, принимающая два аргумента +add a b = a + b + +-- Внимание! +-- Если вы используете ghci (интерактивный интерпретатор Haskell), +-- вам нужно использовать ключевое слово `let`, примерно так: +-- let add a b = a + b + +-- Вызовем нашу функцию +add 1 2 -- 3 + +-- Функцию можно поместить между первым и вторым аргументами, +-- если заключить её имя в обратные кавычки +1 `add` 2 -- 3 + +{- Вы можете также определять функции, имя которых +вообще не содержит букв! Таки функции и называются "операторами", +и, да, вы можете определять свои операторы! +Скажем, оператор целочисленного деления можно определить так -} +(//) a b = a `div` b +35 // 4 -- 8 +{- Здесь оператор заключен в скобки - как говорят, +поставлен в префиксную позицию. +В префиксной позиции оператор можно не только определять, +но и вызывать -} +(+) 1 2 -- 3 + +-- Охранные выражения (guards) порой удобны, +-- если наша функция ветвится +fib x + | x < 2 = x + | otherwise = fib (x - 1) + fib (x - 2) + +{- Сопоставление с образцом (pattern matching) +чем-то напоминает охранные выражения. +Здесь мы видим три определения функции fib. +При вызове функции по имени Haskell использует +первое определение, к образцу которого +"подойдет" набор аргументов -} +fib 1 = 1 +fib 2 = 2 +fib x = fib (x - 1) + fib (x - 2) + +-- Pattern matching для кортежей выглядит так +foo (x, y) = (x + 1, y + 2) + +{- Pattern matching для списков устроен чуть сложнее. +Пусть `x` - первый элемент списка, а `xs` - остальные элементы. +Тогда операции `head` и `tail` могут быть определены так -} +myHead (x:xs) = x +myTail (x:xs) = xs + +-- Функцию отображения мы можем написать так +myMap func [] = [] +myMap func (x:xs) = func x:(myMap func xs) + +-- При сопоставлении происходит привязка +-- элементов значения с именами в образце +fstPlusThird (a : _ : b : _) = a + b +fstPlusThird [1,2,3,4,5] -- 4 +-- Значения, для которых вместо имени указано `_`, +-- игнорируются. Это удобно, когда важен сам факт +-- совпадения образца +oneElem [_] = True +oneElem _ = False + +startsWith x (y:_) = x == y +startsWith _ _ = False + +startsWith 'H' "Hello!" -- True +startsWith 'H' "hello!" -- False + +{- Обратите внимание на тот факт, +что первый аргумент нашей функции `myMap` - тоже функция! +Функции, подобно `myMap`, принимающие другие функции +в качестве параметров, или, скажем, возвращающие функции +в качестве результата, называются +Функциями Высших Порядков (ФВП, High Order Functions, HOF) +-} + +-- Вместе с ФВП часто используются анонимные функции +myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7] +-- Такие функции описываются в виде +-- \arg1 arg1 .. -> expression + +-- Популярные в других языках ФВП присутствуют и в Haskell +map (\x -> x * 10) [1..5] -- [10, 20, 30, 40, 50] +filter (\x -> x > 2) [1..5] -- [3, 4, 5] + +{- Функция свертки +(она же `reduce` или `inject` в других языках) +в Haskell представлены функциями `foldr` и `foldl`. +Суть свертки можно представить так: + +foldl f x0 [x1,x2,x3] -> (f (f (f x0 x1) x2) x3) +foldr f x0 [x1,x2,x3] -> (f x1 (f x2 (f x3 x0))) + +Здесь x0 - начальное значения так называемого "аккумулятора" +-} +-- Эти два вызова дают одинаковый результат +foldr (\x acc -> acc + x) 0 [1..5] -- 15 +foldl (\acc x -> acc + x) 0 [1..5] -- 15 +-- Тут можно даже заменить анонимную функцию на оператор +foldr (+) 0 [1..5] -- 15 +foldl (+) 0 [1..5] -- 15 + +-- Зато здесь разница видна +foldr (\x acc -> (x + 10) : acc) [] [1..3] -- [11, 12, 13] +foldl (\acc x -> (x + 10) : acc) [] [1..3] -- [13, 12, 11] + +{- Часто в качестве начального значения +удобно брать крайнее значение списка (крайнее слева или справа). +Для этого есть пара функций - `foldr1` и `foldl1` -} +foldr1 (+) [1..5] -- 15 +foldl1 (+) [1..5] -- 15 + +---------------------------------------------------- +-- 4. Больше о функциях +---------------------------------------------------- + +{- Каррирование (currying) +Если в Haskell при вызове функции передать не все аргументы, +Функция становится "каррированой" - результатом вызова станет +новая функция, которая при вызове и примет оставшиеся аргументы -} + +add a b = a + b +foo = add 10 -- теперь foo будет принимать число + -- и добавлять к нему 10 +foo 5 -- 15 + +-- Для операторов можно "опустить" любой из двух аргументов +-- Используя этот факт можно определить +-- функцию `foo` из кода выше несколько иначе +foo = (+10) +foo 5 -- 15 + +-- Поупражняемся +map (10-) [1..3] -- [9, 8, 7] +filter (<5) [1..10] -- [1, 2, 3, 4] + +{- Композиция функций +Функция (.) соединяет пару функций в цепочку. +К примеру, можно соединить функцию, добавляющую 10, +с функцией, умножающей на 5 -} +foo = (*5) . (+10) + +-- (5 + 10) * 5 = 75 +foo 5 -- 75 + +{- Управление приоритетом вычисления +В Haskell есть функция `$`, которая применяет +свой первый аргумент ко второму с наименьшим приоритетом +(обычное применение функций имеет наивысший приоритет) +Эта функция часто позволяет избежать использования +"лишних" скобок -} +head (tail (tail "abcd")) -- 'c' +head $ tail $ tail "abcd" -- 'c' +-- того же эффекта иногда можно достичь использованием композиции +(head . tail . tail) "abcd" -- 'c' +head . tail . tail $ "abcd" -- 'c' +{- Тут стоит сразу запомнить, что композиция функций +возвращает именно новую функцию, как в последнем примере. +Т.е. можно делать так -} +third = head . tail . tail +-- но не так +third = head $ tail $ tail -- (head (tail (tail))) - ошибка! + +---------------------------------------------------- +-- 5. Сигнатуры типов +---------------------------------------------------- + +{- Haskell обладает очень сильной системой типов. +И типизация в Haskell - строгая. Каждое выражение имеет тип, +который может быть описан сигнатурой. +Сигнатура записывается в форме +expression :: type signature +-} + +-- Типы примитивов +5 :: Integer +"hello" :: String +True :: Bool + +{- Функции тоже имеют тип +`not` принимает булево значение и возвращает булев результат +not :: Bool -> Bool + +Вот функция двух аргументов +add :: Integer -> Integer -> Integer + +Тут то мы и видим предпосылки к каррированию: тип +на самом деле выглядит так (скобки просто обычно опускаются) +add :: (Integer -> Integer) -> Integer +т.е. функция принимает аргумент, +и возвращает функцию от второго аргумента! -} + +-- Считается хорошим тоном указывать сигнатуру определений, +-- которые доступны другим разработчикам (публичны). Пример: +double :: Integer -> Integer +double x = x * 2 + +---------------------------------------------------- +-- 6. Управление потоком исполнения +---------------------------------------------------- + +-- Выражение `if` +haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome" + +-- Выражение `if` можно записать и в несколько строк. +-- Соблюдайте отступы! +haskell = if 1 == 1 + then "awesome" + else "awful" + +-- Так как `if` - выражение, ветка `else` обязательна! +-- И более того, результаты выражений в ветках `then` и `else` +-- должны иметь одинаковый тип! + +-- `case`-выражение выглядит так +case args of -- парсим аргументы командной строки + "help" -> printHelp + "start" -> startProgram + _ -> putStrLn "bad args" + +-- При вычислении результата `case`-выражения производится +-- сопоставление с образцом: +fib x = case x of + 1 -> 1 + 2 -> 1 + _ -> fib (x - 1) + fib (x - 2) + +-- В Haskell нет циклов - вместо них используются рекурсия, +-- отображение, фильтрация и свертка (map/filter/fold) +map (*2) [1..5] -- [2, 4, 6, 8, 10] + +for array func = map func array +for [0..3] $ \i -> show i -- ["0", "1", "2", "3"] +for [0..3] show -- ["0", "1", "2", "3"] + +---------------------------------------------------- +-- 7. Пользовательские типы данных +---------------------------------------------------- + +-- Создадим свой Haskell-тип данных + +data Color = Red | Blue | Green + +-- Попробуем использовать + +say :: Color -> String +say Red = "You are Red!" +say Blue = "You are Blue!" +say Green = "You are Green!" + +-- Типы могут иметь параметры (параметры типов) + +data Maybe a = Nothing | Just a + +-- Все эти выражения имеют тип `Maybe` +Just "hello" -- :: `Maybe String` +Just 1 -- :: `Maybe Int` +Nothing -- :: `Maybe a` для любого `a` + +-- Типы могут быть достаточно сложными +data Figure = Rectangle (Int, Int) Int Int + | Square (Int, Int) Int + | Point (Int, Int) + +area :: Figure -> Int +area (Point _) = 0 +area (Square _ s) = s * s +area (Rectangle _ w h) = w * h + +---------------------------------------------------- +-- 8. Ввод-вывод в Haskell +---------------------------------------------------- + +-- Полноценно объяснить тему ввода-вывода невозможно +-- без объяснения монад, но для использования в простых случаях +-- вводного описания будет достаточно. + +-- Когда программа на Haskell выполняется, +-- вызывается функция с именем `main`. +-- Эта функция должна вернуть значение типа `IO ()` +-- Например + +main :: IO () +main = putStrLn $ "Hello, sky! " ++ (say Blue) +-- `putStrLn` имеет тип `String -> IO ()` + +-- Проще всего реализовать программу с вводом-выводом (IO), +-- если вы реализуете функцию с типом `String -> String`. +-- Далее ФВП +-- interact :: (String -> String) -> IO () +-- сделает всё за нас! + +countLines :: String -> String +countLines = show . length . lines +-- здесь `lines` разделяет строку на список строк +-- по символу перевода строки + +main' :: IO () +main' = interact countLines + +{- Вы можете думать о типе `IO ()`, +как о некотором представлении последовательности +действий, которые должен совершить компьютер. +Такое представление напоминает программу +на императивном языке программирования. Для описания +такой последовательности используется `do`-нотация -} + +sayHello :: IO () +sayHello = do + putStrLn "What is your name?" + name <- getLine -- запрашиваем строку и связываем с "name" + putStrLn $ "Hello, " ++ name + +-- Упражнение: +-- напишите свою реализацию функции `interact`, +-- которая запрашивает и обрабатывает только одну строку + +{- Код функции `sayHello` не будет исполняться +при её определении. Единственное место, где IO-действия +могут быть произведены - функция `main`! +Чтобы эта программа выполнила действия в функции `sayHello`, +закомментируйте предыдущее определение функции `main` +и добавьте новое определение: + +main = sayHello -} + +{- Давайте подробнее рассмотрим, как работает функция `getLine` +Её тип: + getLine :: IO String +Вы можете думать, что значение типа `IO a` представляет +собой компьютерную программу, в результате выполнения которой +генерируется значение типа `a`, в дополнение +к остальным эффектам, производимым при выполнении - таким как +печать текста на экран. Это значение типа `a` мы можем +сохранить с помощью оператора `<-`. Мы даже можем реализовать +свое действие, возвращающее значение: -} + +action :: IO String +action = do + putStrLn "This is a line. Duh" + input1 <- getLine + input2 <- getLine + -- Тип блока `do` будет соответствовать типу последнего + -- выполненного в блоке выражения. + -- Заметим, что `return` - не ключевое слово, а функция + -- типа `a -> IO a` + return (input1 ++ "\n" ++ input2) -- return :: String -> IO String + +-- Теперь это действие можно использовать вместо `getLine`: + +main'' = do + putStrLn "I will echo two lines!" + result <- action + putStrLn result + putStrLn "This was all, folks!" + +{- Тип `IO` - пример "монады". Языку Haskell нужны монады, +чтобы оставаться преимущественно чистым функциональным языком. +Любые функции, взаимодействующие с внешним миром +(производящие ввод-вывод) имеют `IO` в своих сигнатурах. +Это позволяет судить о функции как о "чистой" - такая не будет +производить ввод-вывод. В ином случая функция - не "чистая". + +Такой подход позволяет очень просто разрабатывать многопоточные +программы - чистые функции, запущенные параллельно +не будут конфликтовать между собой в борьбе за ресурсы. -} + +---------------------------------------------------- +-- 9. Haskell REPL +---------------------------------------------------- + +{- Интерактивная консоль Haskell запускается командой `ghci`. +Теперь можно вводить строки кода на Haskell. +Связывание значений с именами производится +с помощью выражения `let`: -} + +let foo = 5 + +-- Тип значения или выражения можно узнать +-- с помощью команды `:t`: + +>:t foo +foo :: Integer + +-- Также можно выполнять действия с типом `IO ()` + +> sayHello +What is your name? +Friend! +Hello, Friend! + +``` + +Многое о Haskell, например классы типов и монады невозможно уместить в столь короткую статью. Огромное количество очень интересных идей лежит в основе языка, и именно благодаря этому фундаменту на языке так приятно писать код. Позволю себе привести ещё один маленький пример кода на Haskell - реализацию быстрой сортировки: + +```haskell +qsort [] = [] +qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater + where lesser = filter (< p) xs + greater = filter (>= p) xs +``` + +Haskell прост в установке, забирайте [здесь](http://www.haskell.org/platform/) и пробуйте! Это же так интересно!. + +Более глубокое погрузиться в язык позволят прекрасные книги +[Learn you a Haskell](http://learnyouahaskell.com/) и +[Real World Haskell](http://book.realworldhaskell.org/). + +[autor]: http://adit.io имеется в виду автор оригинального текста Adit Bhargava *(примечание переводчика)* diff --git a/ru-ru/julia-ru.html.markdown b/ru-ru/julia-ru.html.markdown new file mode 100644 index 00000000..cd55e116 --- /dev/null +++ b/ru-ru/julia-ru.html.markdown @@ -0,0 +1,750 @@ +--- +language: julia +contributors: + - ["Leah Hanson", "http://leahhanson.us"] +translators: + - ["Sergey Skovorodkin", "https://github.com/skovorodkin"] +filename: learnjulia-ru.jl +lang: ru-ru +--- + +Julia — гомоиконный функциональный язык программирования для технических расчётов. +Несмотря на полную поддержку гомоиконных макросов, функций первого класса и конструкций управления низкого уровня, этот язык так же прост в изучении и применении, как и Python. + +Документ описывает текущую dev-версию Julia от 18-о октября 2013 года. + +```ruby + +# Однострочные комментарии начинаются со знака решётки. + +#################################################### +## 1. Примитивные типы данных и операторы +#################################################### + +# Всё в Julia — выражение. + +# Простые численные типы +3 # => 3 (Int64) +3.2 # => 3.2 (Float64) +2 + 1im # => 2 + 1im (Complex{Int64}) +2//3 # => 2//3 (Rational{Int64}) + +# Доступны все привычные инфиксные операторы +1 + 1 # => 2 +8 - 1 # => 7 +10 * 2 # => 20 +35 / 5 # => 7.0 +5 / 2 # => 2.5 # деление Int на Int всегда возвращает Float +div(5, 2) # => 2 # для округления к нулю используется div +5 \ 35 # => 7.0 +2 ^ 2 # => 4 # возведение в степень +12 % 10 # => 2 + +# С помощью скобок можно изменить приоритет операций +(1 + 3) * 2 # => 8 + +# Побитовые операторы +~2 # => -3 # НЕ (NOT) +3 & 5 # => 1 # И (AND) +2 | 4 # => 6 # ИЛИ (OR) +2 $ 4 # => 6 # сложение по модулю 2 (XOR) +2 >>> 1 # => 1 # логический сдвиг вправо +2 >> 1 # => 1 # арифметический сдвиг вправо +2 << 1 # => 4 # логический/арифметический сдвиг влево + +# Функция bits возвращает бинарное представление числа +bits(12345) +# => "0000000000000000000000000000000000000000000000000011000000111001" +bits(12345.0) +# => "0100000011001000000111001000000000000000000000000000000000000000" + +# Логические значения являются примитивами +true +false + +# Булевы операторы +!true # => false +!false # => true +1 == 1 # => true +2 == 1 # => false +1 != 1 # => false +2 != 1 # => true +1 < 10 # => true +1 > 10 # => false +2 <= 2 # => true +2 >= 2 # => true +# Сравнения можно объединять цепочкой +1 < 2 < 3 # => true +2 < 3 < 2 # => false + +# Строки объявляются с помощью двойных кавычек — " +"This is a string." + +# Символьные литералы создаются с помощью одинарных кавычек — ' +'a' + +# Строки индексируются как массивы символов +"This is a string"[1] # => 'T' # Индексы начинаются с единицы +# Индексирование не всегда правильно работает для UTF8-строк, +# поэтому рекомендуется использовать итерирование (map, for-циклы и т.п.). + +# Для строковой интерполяции используется знак доллара ($): +"2 + 2 = $(2 + 2)" # => "2 + 2 = 4" +# В скобках можно использовать любое выражение языка. + +# Другой способ форматирования строк — макрос printf +@printf "%d is less than %f" 4.5 5.3 # 5 is less than 5.300000 + +#################################################### +## 2. Переменные и коллекции +#################################################### + +# Вывод +println("I'm Julia. Nice to meet you!") + +# Переменные инициализируются без предварительного объявления +some_var = 5 # => 5 +some_var # => 5 + +# Попытка доступа к переменной до инициализации вызывает ошибку +try + some_other_var # => ERROR: some_other_var not defined +catch e + println(e) +end + +# Имена переменных начинаются с букв. +# После первого символа можно использовать буквы, цифры, +# символы подчёркивания и восклицательные знаки. +SomeOtherVar123! = 6 # => 6 + +# Допустимо использование unicode-символов +☃ = 8 # => 8 +# Это особенно удобно для математических обозначений +2 * π # => 6.283185307179586 + +# Рекомендации по именованию: +# * имена переменных в нижнем регистре, слова разделяются символом +# подчёркивания ('\_'); +# +# * для имён типов используется CamelCase; +# +# * имена функций и макросов в нижнем регистре +# без разделения слов символом подчёркивания; +# +# * имя функции, изменяющей переданные ей аргументы (in-place function), +# оканчивается восклицательным знаком. + +# Массив хранит последовательность значений, индексируемых с единицы до n: +a = Int64[] # => пустой массив Int64-элементов + +# Одномерный массив объявляется разделёнными запятой значениями. +b = [4, 5, 6] # => массив из трёх Int64-элементов: [4, 5, 6] +b[1] # => 4 +b[end] # => 6 + +# Строки двумерного массива разделяются точкой с запятой. +# Элементы строк разделяются пробелами. +matrix = [1 2; 3 4] # => 2x2 Int64 Array: [1 2; 3 4] + +# push! и append! добавляют в список новые элементы +push!(a,1) # => [1] +push!(a,2) # => [1,2] +push!(a,4) # => [1,2,4] +push!(a,3) # => [1,2,4,3] +append!(a,b) # => [1,2,4,3,4,5,6] + +# pop! удаляет из списка последний элемент +pop!(b) # => возвращает 6; массив b снова равен [4,5] + +# Вернём 6 обратно +push!(b,6) # b снова [4,5,6]. + +a[1] # => 1 # индексы начинаются с единицы! + +# Последний элемент можно получить с помощью end +a[end] # => 6 + +# Операции сдвига +shift!(a) # => 1 and a is now [2,4,3,4,5,6] +unshift!(a,7) # => [7,2,4,3,4,5,6] + +# Восклицательный знак на конце названия функции означает, +# что функция изменяет переданные ей аргументы. +arr = [5,4,6] # => массив из 3 Int64-элементов: [5,4,6] +sort(arr) # => [4,5,6]; но arr равен [5,4,6] +sort!(arr) # => [4,5,6]; а теперь arr — [4,5,6] + +# Попытка доступа за пределами массива выбрасывает BoundsError +try + a[0] # => ERROR: BoundsError() in getindex at array.jl:270 + a[end+1] # => ERROR: BoundsError() in getindex at array.jl:270 +catch e + println(e) +end + +# Вывод ошибок содержит строку и файл, где произошла ошибка, +# даже если это случилось в стандартной библиотеке. +# Если вы собрали Julia из исходных кодов, +# то найти эти файлы можно в директории base. + +# Создавать массивы можно из последовательности +a = [1:5] # => массив из 5 Int64-элементов: [1,2,3,4,5] + +# Срезы +a[1:3] # => [1, 2, 3] +a[2:] # => [2, 3, 4, 5] +a[2:end] # => [2, 3, 4, 5] + +# splice! удаляет элемент из массива +# Remove elements from an array by index with splice! +arr = [3,4,5] +splice!(arr,2) # => 4 ; arr теперь равен [3,5] + +# append! объединяет списки +b = [1,2,3] +append!(a,b) # теперь a равен [1, 2, 3, 4, 5, 1, 2, 3] + +# Проверка на вхождение +in(1, a) # => true + +# Длина списка +length(a) # => 8 + +# Кортеж — неизменяемая структура. +tup = (1, 2, 3) # => (1,2,3) # кортеж (Int64,Int64,Int64). +tup[1] # => 1 +try: + tup[1] = 3 # => ERROR: no method setindex!((Int64,Int64,Int64),Int64,Int64) +catch e + println(e) +end + +# Многие функции над списками работают и для кортежей +length(tup) # => 3 +tup[1:2] # => (1,2) +in(2, tup) # => true + +# Кортежи можно распаковывать в переменные +a, b, c = (1, 2, 3) # => (1,2,3) # a = 1, b = 2 и c = 3 + +# Скобки из предыдущего примера можно опустить +d, e, f = 4, 5, 6 # => (4,5,6) + +# Кортеж из одного элемента не равен значению этого элемента +(1,) == 1 # => false +(1) == 1 # => true + +# Обмен значений +e, d = d, e # => (5,4) # d = 5, e = 4 + + +# Словари содержат ассоциативные массивы +empty_dict = Dict() # => Dict{Any,Any}() + +# Для создания словаря можно использовать литерал +filled_dict = ["one"=> 1, "two"=> 2, "three"=> 3] +# => Dict{ASCIIString,Int64} + +# Значения ищутся по ключу с помощью оператора [] +filled_dict["one"] # => 1 + +# Получить все ключи +keys(filled_dict) +# => KeyIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2]) +# Заметьте, словарь не запоминает порядок, в котором добавляются ключи. + +# Получить все значения. +values(filled_dict) +# => ValueIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2]) +# То же касается и порядка значений. + +# Проверка вхождения ключа в словарь +in(("one", 1), filled_dict) # => true +in(("two", 3), filled_dict) # => false +haskey(filled_dict, "one") # => true +haskey(filled_dict, 1) # => false + +# Попытка обратиться к несуществующему ключу выбросит ошибку +try + filled_dict["four"] # => ERROR: key not found: four in getindex at dict.jl:489 +catch e + println(e) +end + +# Используйте метод get со значением по умолчанию, чтобы избежать этой ошибки +# get(dictionary,key,default_value) +get(filled_dict,"one",4) # => 1 +get(filled_dict,"four",4) # => 4 + +# Для коллекций неотсортированных уникальных элементов используйте Set +empty_set = Set() # => Set{Any}() +# Инициализация множества +filled_set = Set(1,2,2,3,4) # => Set{Int64}(1,2,3,4) + +# Добавление элементов +push!(filled_set,5) # => Set{Int64}(5,4,2,3,1) + +# Проверка вхождения элементов во множество +in(2, filled_set) # => true +in(10, filled_set) # => false + +# Функции для получения пересечения, объединения и разницы. +other_set = Set(3, 4, 5, 6) # => Set{Int64}(6,4,5,3) +intersect(filled_set, other_set) # => Set{Int64}(3,4,5) +union(filled_set, other_set) # => Set{Int64}(1,2,3,4,5,6) +setdiff(Set(1,2,3,4),Set(2,3,5)) # => Set{Int64}(1,4) + + +#################################################### +## 3. Поток управления +#################################################### + +# Создадим переменную +some_var = 5 + +# Выражение if. Отступы не имеют значения. +if some_var > 10 + println("some_var is totally bigger than 10.") +elseif some_var < 10 # Необязательная ветка elseif. + println("some_var is smaller than 10.") +else # else-ветка также опциональна. + println("some_var is indeed 10.") +end +# => prints "some var is smaller than 10" + + +# Цикл for проходит по итерируемым объектам +# Примеры итерируемых типов: Range, Array, Set, Dict и String. +for animal=["dog", "cat", "mouse"] + println("$animal is a mammal") + # Для вставки значения переменной или выражения в строку используется $ +end +# Выведет: +# dog is a mammal +# cat is a mammal +# mouse is a mammal + +# Другой вариант записи. +for animal in ["dog", "cat", "mouse"] + println("$animal is a mammal") +end +# Выведет: +# dog is a mammal +# cat is a mammal +# mouse is a mammal + +for a in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"] + println("$(a[1]) is a $(a[2])") +end +# Выведет: +# dog is a mammal +# cat is a mammal +# mouse is a mammal + +for (k,v) in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"] + println("$k is a $v") +end +# Выведет: +# dog is a mammal +# cat is a mammal +# mouse is a mammal + +# Цикл while выполняется до тех пор, пока верно условие +x = 0 +while x < 4 + println(x) + x += 1 # Короткая запись x = x + 1 +end +# Выведет: +# 0 +# 1 +# 2 +# 3 + +# Обработка исключений +try + error("help") +catch e + println("caught it $e") +end +# => caught it ErrorException("help") + + +#################################################### +## 4. Функции +#################################################### + +# Для определения новой функции используется ключевое слово 'function' +#function имя(аргументы) +# тело... +#end +function add(x, y) + println("x is $x and y is $y") + + # Функция возвращает значение последнего выражения + x + y +end + +add(5, 6) # => Вернёт 11, напечатав "x is 5 and y is 6" + +# Функция может принимать переменное количество позиционных аргументов. +function varargs(args...) + return args + # для возвращения из функции в любом месте используется 'return' +end +# => varargs (generic function with 1 method) + +varargs(1,2,3) # => (1,2,3) + +# Многоточие (...) — это splat. +# Мы только что воспользовались им в определении функции. +# Также его можно использовать при вызове функции, +# где он преобразует содержимое массива или кортежа в список аргументов. +Set([1,2,3]) # => Set{Array{Int64,1}}([1,2,3]) # формирует множество массивов +Set([1,2,3]...) # => Set{Int64}(1,2,3) # эквивалентно Set(1,2,3) + +x = (1,2,3) # => (1,2,3) +Set(x) # => Set{(Int64,Int64,Int64)}((1,2,3)) # множество кортежей +Set(x...) # => Set{Int64}(2,3,1) + + +# Опциональные позиционные аргументы +function defaults(a,b,x=5,y=6) + return "$a $b and $x $y" +end + +defaults('h','g') # => "h g and 5 6" +defaults('h','g','j') # => "h g and j 6" +defaults('h','g','j','k') # => "h g and j k" +try + defaults('h') # => ERROR: no method defaults(Char,) + defaults() # => ERROR: no methods defaults() +catch e + println(e) +end + +# Именованные аргументы +function keyword_args(;k1=4,name2="hello") # обратите внимание на ; + return ["k1"=>k1,"name2"=>name2] +end + +keyword_args(name2="ness") # => ["name2"=>"ness","k1"=>4] +keyword_args(k1="mine") # => ["k1"=>"mine","name2"=>"hello"] +keyword_args() # => ["name2"=>"hello","k2"=>4] + +# В одной функции можно совмещать все виды аргументов +function all_the_args(normal_arg, optional_positional_arg=2; keyword_arg="foo") + println("normal arg: $normal_arg") + println("optional arg: $optional_positional_arg") + println("keyword arg: $keyword_arg") +end + +all_the_args(1, 3, keyword_arg=4) +# Выведет: +# normal arg: 1 +# optional arg: 3 +# keyword arg: 4 + +# Функции в Julia первого класса +function create_adder(x) + adder = function (y) + return x + y + end + return adder +end + +# Анонимная функция +(x -> x > 2)(3) # => true + +# Эта функция идентичная предыдущей версии create_adder +function create_adder(x) + y -> x + y +end + +# Если есть желание, можно воспользоваться полным вариантом +function create_adder(x) + function adder(y) + x + y + end + adder +end + +add_10 = create_adder(10) +add_10(3) # => 13 + + +# Встроенные функции высшего порядка +map(add_10, [1,2,3]) # => [11, 12, 13] +filter(x -> x > 5, [3, 4, 5, 6, 7]) # => [6, 7] + +# Списковые сборки +[add_10(i) for i=[1, 2, 3]] # => [11, 12, 13] +[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] + +#################################################### +## 5. Типы +#################################################### + +# Julia has a type system. +# Каждое значение имеет тип, но переменные не определяют тип значения. +# Функция `typeof` возвращает тип значения. +typeof(5) # => Int64 + +# Types are first-class values +# Типы являются значениями первого класса +typeof(Int64) # => DataType +typeof(DataType) # => DataType +# Тип DataType представляет типы, включая себя самого. + +# Типы используются в качестве документации, для оптимизации и организации. +# Статически типы не проверяются. + +# Пользователь может определять свои типы +# Типы похожи на структуры в других языках +# Новые типы определяются с помощью ключевого слова `type` + +# type Name +# field::OptionalType +# ... +# end +type Tiger + taillength::Float64 + coatcolor # отсутствие типа равносильно `::Any` +end + +# Аргументы конструктора по умолчанию — свойства типа +# в порядке их определения. +tigger = Tiger(3.5,"orange") # => Tiger(3.5,"orange") + +# Тип объекта по сути является конструктором значений такого типа +sherekhan = typeof(tigger)(5.6,"fire") # => Tiger(5.6,"fire") + +# Эти типы, похожие на структуры, называются конкретными. +# Можно создавать объекты таких типов, но не их подтипы. +# Другой вид типов — абстрактные типы. + +# abstract Name +abstract Cat # просто имя и точка в иерархии типов + +# Объекты абстрактных типов создавать нельзя, +# но зато от них можно наследовать подтипы. +# Например, Number — это абстрактный тип. +subtypes(Number) # => 6 элементов в массиве Array{Any,1}: + # Complex{Float16} + # Complex{Float32} + # Complex{Float64} + # Complex{T<:Real} + # ImaginaryUnit + # Real +subtypes(Cat) # => пустой массив Array{Any,1} + +# У всех типов есть супертип. Для его определения есть функция `super`. +typeof(5) # => Int64 +super(Int64) # => Signed +super(Signed) # => Real +super(Real) # => Number +super(Number) # => Any +super(super(Signed)) # => Number +super(Any) # => Any +# Все эти типы, за исключением Int64, абстрактные. + +# Для создания подтипа используется оператор <: +type Lion <: Cat # Lion — это подтип Cat + mane_color + roar::String +end + +# У типа может быть несколько конструкторов. +# Для создания нового определите функцию с именем, как у типа, +# и вызовите имеющийся конструктор. +Lion(roar::String) = Lion("green",roar) +# Мы создали внешний (т.к. он находится вне определения типа) конструктор. + +type Panther <: Cat # Panther — это тоже подтип Cat + eye_color + + # Определим свой конструктор вместо конструктора по умолчанию + Panther() = new("green") +end +# Использование внутренних конструкторов позволяет +# определять, как будут создаваться объекты типов. +# Но по возможности стоит пользоваться внешними конструкторами. + +#################################################### +## 6. Мультиметоды +#################################################### + +# Все именованные функции являются generic-функциями, +# т.е. все они состоят из разных методов. +# Каждый конструктор типа Lion — это метод generic-функции Lion. + +# Приведём пример без использования конструкторов, создадим функцию meow + +# Определения Lion, Panther и Tiger +function meow(animal::Lion) + animal.roar # доступ к свойству типа через точку +end + +function meow(animal::Panther) + "grrr" +end + +function meow(animal::Tiger) + "rawwwr" +end + +# Проверка +meow(tigger) # => "rawwr" +meow(Lion("brown","ROAAR")) # => "ROAAR" +meow(Panther()) # => "grrr" + +# Вспомним иерархию типов +issubtype(Tiger,Cat) # => false +issubtype(Lion,Cat) # => true +issubtype(Panther,Cat) # => true + +# Определим функцию, принимающую на вход объекты типа Cat +function pet_cat(cat::Cat) + println("The cat says $(meow(cat))") +end + +pet_cat(Lion("42")) # => выведет "The cat says 42" +try + pet_cat(tigger) # => ERROR: no method pet_cat(Tiger,) +catch e + println(e) +end + +# В объектно-ориентированных языках распространена одиночная диспетчеризация — +# подходящий метод выбирается на основе типа первого аргумента. +# В Julia все аргументы участвуют в выборе нужного метода. + +# Чтобы понять разницу, определим функцию с несколькими аргументами. +function fight(t::Tiger,c::Cat) + println("The $(t.coatcolor) tiger wins!") +end +# => fight (generic function with 1 method) + +fight(tigger,Panther()) # => выведет The orange tiger wins! +fight(tigger,Lion("ROAR")) # => выведет The orange tiger wins! + +# Переопределим поведение функции, если Cat-объект является Lion-объектом +fight(t::Tiger,l::Lion) = println("The $(l.mane_color)-maned lion wins!") +# => fight (generic function with 2 methods) + +fight(tigger,Panther()) # => выведет The orange tiger wins! +fight(tigger,Lion("ROAR")) # => выведет The green-maned lion wins! + +# Драться можно не только с тиграми! +fight(l::Lion,c::Cat) = println("The victorious cat says $(meow(c))") +# => fight (generic function with 3 methods) + +fight(Lion("balooga!"),Panther()) # => выведет The victorious cat says grrr +try + fight(Panther(),Lion("RAWR")) # => ERROR: no method fight(Panther,Lion) +catch +end + +# Вообще, пускай кошачьи могут первыми проявлять агрессию +fight(c::Cat,l::Lion) = println("The cat beats the Lion") +# => Warning: New definition +# fight(Cat,Lion) at none:1 +# is ambiguous with +# fight(Lion,Cat) at none:2. +# Make sure +# fight(Lion,Lion) +# is defined first. +#fight (generic function with 4 methods) + +# Предупреждение говорит, что неясно, какой из методов вызывать: +fight(Lion("RAR"),Lion("brown","rarrr")) # => выведет The victorious cat says rarrr +# Результат может оказаться разным в разных версиях Julia + +fight(l::Lion,l2::Lion) = println("The lions come to a tie") +fight(Lion("RAR"),Lion("brown","rarrr")) # => выведет The lions come to a tie + + +# Под капотом +# Язык позволяет посмотреть на сгенерированные ассемблерный и LLVM-код. + +square_area(l) = l * l # square_area (generic function with 1 method) + +square_area(5) #25 + +# Что происходит, когда мы передаём функции square_area целое число? +code_native(square_area, (Int32,)) + # .section __TEXT,__text,regular,pure_instructions + # Filename: none + # Source line: 1 # Вводная часть + # push RBP + # mov RBP, RSP + # Source line: 1 + # movsxd RAX, EDI # + # imul RAX, RAX # + # pop RBP # + # ret # + +code_native(square_area, (Float32,)) + # .section __TEXT,__text,regular,pure_instructions + # Filename: none + # Source line: 1 + # push RBP + # mov RBP, RSP + # Source line: 1 + # vmulss XMM0, XMM0, XMM0 # Произведение чисел одинарной точности (AVX) + # pop RBP + # ret + +code_native(square_area, (Float64,)) + # .section __TEXT,__text,regular,pure_instructions + # Filename: none + # Source line: 1 + # push RBP + # mov RBP, RSP + # Source line: 1 + # vmulsd XMM0, XMM0, XMM0 # Произведение чисел двойной точности (AVX) + # pop RBP + # ret + # +# Если хотя бы один из аргументов является числом с плавающей запятой, +# то Julia будет использовать соответствующие инструкции. +# Вычислим площать круга +circle_area(r) = pi * r * r # circle_area (generic function with 1 method) +circle_area(5) # 78.53981633974483 + +code_native(circle_area, (Int32,)) + # .section __TEXT,__text,regular,pure_instructions + # Filename: none + # Source line: 1 + # push RBP + # mov RBP, RSP + # Source line: 1 + # vcvtsi2sd XMM0, XMM0, EDI # Загрузить целое число (r) + # movabs RAX, 4593140240 # Загрузить pi + # vmulsd XMM1, XMM0, QWORD PTR [RAX] # pi * r + # vmulsd XMM0, XMM0, XMM1 # (pi * r) * r + # pop RBP + # ret + # + +code_native(circle_area, (Float64,)) + # .section __TEXT,__text,regular,pure_instructions + # Filename: none + # Source line: 1 + # push RBP + # mov RBP, RSP + # movabs RAX, 4593140496 + # Source line: 1 + # vmulsd XMM1, XMM0, QWORD PTR [RAX] + # vmulsd XMM0, XMM1, XMM0 + # pop RBP + # ret + # +``` + +## Что дальше? + +Для более подробной информации читайте [документацию по языку](http://docs.julialang.org/en/latest/manual/) + +Если вам нужна помощь, задавайте вопросы в [списке рассылки](https://groups.google.com/forum/#!forum/julia-users). diff --git a/ru-ru/objective-c-ru.html.markdown b/ru-ru/objective-c-ru.html.markdown new file mode 100644 index 00000000..72e3b9e0 --- /dev/null +++ b/ru-ru/objective-c-ru.html.markdown @@ -0,0 +1,317 @@ +--- +language: Objective-C +filename: LearnObjectiveC.m +contributors: + - ["Eugene Yagrushkin", "www.about.me/yagrushkin"] + - ["Yannick Loriot", "https://github.com/YannickL"] +translators: + - ["Evlogy Sutormin", "http://evlogii.com"] +lang: ru-ru +--- + +Objective-C — компилируемый объектно-ориентированный язык программирования, используемый корпорацией Apple, +построенный на основе языка Си и парадигм Smalltalk. +В частности, объектная модель построена в стиле Smalltalk — то есть объектам посылаются сообщения. + +```cpp +// Однострочный комментарий + +/* +Многострочный +комментарий +*/ + +// Импорт файлов фреймворка Foundation с помощью #import +#import <Foundation/Foundation.h> +#import "MyClass.h" + +// Точка входа в программу это функция main, +// которая возвращает целый тип integer +int main (int argc, const char * argv[]) +{ + // Создание autorelease pool для управления памятью + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // Используйте NSLog для печати в консоль + NSLog(@"Hello World!"); // Напечатает строку "Hello World!" + + /////////////////////////////////////// + // Типы и переменные + /////////////////////////////////////// + + // Простое объявление + int myPrimitive1 = 1; + long myPrimitive2 = 234554664565; + + // Помещайте * в начало названия объекта для строго типизированного объявления + MyClass *myObject1 = nil; // Строгая типизация + id myObject2 = nil; // Слабая типизация + + NSLog(@"%@ and %@", myObject1, [myObject2 description]); // напечатает "(null) and (null)" + // %@ – это объект + // 'description' это общий для всех объектов метод вывода данных + + // Строка + NSString *worldString = @"World"; + NSLog(@"Hello %@!", worldString); // напечатает "Hello World!" + + // Символьные литералы + NSNumber *theLetterZNumber = @'Z'; + char theLetterZ = [theLetterZNumber charValue]; + NSLog(@"%c", theLetterZ); + + // Целочисленный литералы + NSNumber *fortyTwoNumber = @42; + int fortyTwo = [fortyTwoNumber intValue]; + NSLog(@"%i", fortyTwo); + + // Беззнаковый целочисленный литерал + NSNumber *fortyTwoUnsignedNumber = @42U; + unsigned int fortyTwoUnsigned = [fortyTwoUnsignedNumber unsignedIntValue]; + NSLog(@"%u", fortyTwoUnsigned); + + NSNumber *fortyTwoShortNumber = [NSNumber numberWithShort:42]; + short fortyTwoShort = [fortyTwoShortNumber shortValue]; + NSLog(@"%hi", fortyTwoShort); + + NSNumber *fortyTwoLongNumber = @42L; + long fortyTwoLong = [fortyTwoLongNumber longValue]; + NSLog(@"%li", fortyTwoLong); + + // Вещественный литерал + NSNumber *piFloatNumber = @3.141592654F; + float piFloat = [piFloatNumber floatValue]; + NSLog(@"%f", piFloat); + + NSNumber *piDoubleNumber = @3.1415926535; + double piDouble = [piDoubleNumber doubleValue]; + NSLog(@"%f", piDouble); + + // BOOL (булевый) литерал + NSNumber *yesNumber = @YES; + NSNumber *noNumber = @NO; + + // Массив + NSArray *anArray = @[@1, @2, @3, @4]; + NSNumber *thirdNumber = anArray[2]; + NSLog(@"Third number = %@", thirdNumber); // Print "Third number = 3" + + // Словарь + NSDictionary *aDictionary = @{ @"key1" : @"value1", @"key2" : @"value2" }; + NSObject *valueObject = aDictionary[@"A Key"]; + NSLog(@"Object = %@", valueObject); // Напечатает "Object = (null)" + + /////////////////////////////////////// + // Операторы + /////////////////////////////////////// + + // Операторы работают также как в Си. + // Например: + 2 + 5; // => 7 + 4.2f + 5.1f; // => 9.3f + 3 == 2; // => 0 (НЕТ) + 3 != 2; // => 1 (ДА) + 1 && 1; // => 1 (логическое И) + 0 || 1; // => 1 (логическое ИЛИ) + ~0x0F; // => 0xF0 (побитовое отрицание) + 0x0F & 0xF0; // => 0x00 (побитовое И) + 0x01 << 1; // => 0x02 (побитовый сдвиг влево (на 1)) + + /////////////////////////////////////// + // Структуры ветвления + /////////////////////////////////////// + + // Условный оператор + if (NO) + { + NSLog(@"I am never run"); + } else if (0) + { + NSLog(@"I am also never run"); + } else + { + NSLog(@"I print"); + } + + // Ветвление с множественным выбором + switch (2) + { + case 0: + { + NSLog(@"I am never run"); + } break; + case 1: + { + NSLog(@"I am also never run"); + } break; + default: + { + NSLog(@"I print"); + } break; + } + + // Цикл с предусловием + int ii = 0; + while (ii < 4) + { + NSLog(@"%d,", ii++); // ii++ инкрементирует ii после передачи значения + } // => напечатает "0," + // "1," + // "2," + // "3," + + // Цикл со счётчиком + int jj; + for (jj=0; jj < 4; jj++) + { + NSLog(@"%d,", jj); + } // => напечатает "0," + // "1," + // "2," + // "3," + + // // Цикл просмотра + NSArray *values = @[@0, @1, @2, @3]; + for (NSNumber *value in values) + { + NSLog(@"%@,", value); + } // => напечатает "0," + // "1," + // "2," + // "3," + + // Обработка исключений + @try + { + // Ваше исключение здесь + @throw [NSException exceptionWithName:@"FileNotFoundException" + reason:@"File Not Found on System" userInfo:nil]; + } @catch (NSException * e) + { + NSLog(@"Exception: %@", e); + } @finally + { + NSLog(@"Finally"); + } // => напечатает "Exception: File Not Found on System" + // "Finally" + + /////////////////////////////////////// + // Объекты + /////////////////////////////////////// + + // Создание объектов через выделение памяти и инициализацию. + // Объект не является полнофункциональным пока обе части не выполнятся. + MyClass *myObject = [[MyClass alloc] init]; + + // В Objective-C можель ООП базируется на передаче сообщений. + // В Objective-C Вы не просто вызваете метод; вы посылаете сообщение. + [myObject instanceMethodWithParameter:@"Steve Jobs"]; + + // Очищайте память, перед завершением работы программы. + [pool drain]; + + // Конец программы. + return 0; +} + +/////////////////////////////////////// +// Классы и функции +/////////////////////////////////////// + +// Объявляйте свой класс в файле МойКласс.h +// Синтаксис объявления: +// @interface ИмяКласса : ИмяКлассаРодителя <ИмплементируемыеПротоколы> +// { +// Объявление переменных; +// } +// -/+ (тип) Объявление метода(ов). +// @end + + +@interface MyClass : NSObject <MyProtocol> +{ + int count; + id data; + NSString *name; +} +// При объявлении свойств сразу генерируются геттер и сеттер +@property int count; +@property (copy) NSString *name; // Скопировать объект в ходе присвоения. +@property (readonly) id data; // Генерация только геттера + +// Методы ++/- (return type)methodSignature:(Parameter Type *)parameterName; + +// + для методов класса ++ (NSString *)classMethod; + +// - для метода объекта +- (NSString *)instanceMethodWithParameter:(NSString *)string; +- (NSNumber *)methodAParameterAsString:(NSString*)string andAParameterAsNumber:(NSNumber *)number; + +@end + +// Имплементируйте методы в файле МойКласс.m: + +@implementation MyClass + +// Вызывается при высвобождении памяти под объектом +- (void)dealloc +{ +} + +// Конструкторы – это способ осздания объектов класса. +// Это обычный конструктор вызываемый при создании объекта клсааа. +- (id)init +{ + if ((self = [super init])) + { + self.count = 1; + } + return self; +} + ++ (NSString *)classMethod +{ + return [[self alloc] init]; +} + +- (NSString *)instanceMethodWithParameter:(NSString *)string +{ + return @"New string"; +} + +- (NSNumber *)methodAParameterAsString:(NSString*)string andAParameterAsNumber:(NSNumber *)number +{ + return @42; +} + +// Методы объявленные в МyProtocol (см. далее) +- (void)myProtocolMethod +{ + // имплементация +} + +@end + +/* + * Протокол объявляет методы которые должны быть имплементированы + * Протокол не является классом. Он просто определяет интерфейс, + * который должен быть имплементирован. + */ + +@protocol MyProtocol + - (void)myProtocolMethod; +@end +``` +## На почитать + +[Wikipedia Objective-C](http://en.wikipedia.org/wiki/Objective-C) + +[Learning Objective-C](http://developer.apple.com/library/ios/referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/) + +[iOS For High School Students: Getting Started](http://www.raywenderlich.com/5600/ios-for-high-school-students-getting-started) + +[iOS разработчик: Обзор книг для новичка](http://habrahabr.ru/post/166213/) + +[Хочешь быть iOS разработчиком? Будь им!](http://www.pvsm.ru/ios/12662/print/) diff --git a/ru-ru/php-ru.html.markdown b/ru-ru/php-ru.html.markdown new file mode 100644 index 00000000..edcac4dd --- /dev/null +++ b/ru-ru/php-ru.html.markdown @@ -0,0 +1,663 @@ +--- +language: php +contributors: + - ["Malcolm Fell", "http://emarref.net/"] + - ["Trismegiste", "https://github.com/Trismegiste"] +translators: + - ["SlaF", "https://github.com/SlaF"] +lang: ru-ru +filename: learnphp-ru.php +--- + +Этот документ описывает версию PHP 5 и выше. + +```php +<?php // PHP код должен быть заключен в теги <?php + +// Если ваш файл содержит только PHP код, то можно +// пропустить закрывающийся ?> + +// А так начинаются комментарии + +# Это тоже комментарий но // предпочтительнее + +/* + Окруженный /* и */ текст превращается + в многострочный комментарий +*/ + +// Используйте "echo" или "print" для вывода. +print('Hello '); // Напечатать "Hello " без перевода строки + +// () необязательно применять для print и echo +echo "World\n"; // Печатать "World" и перейти на новую строку. +// (все утверждения должны заканчиваться ;) + +// Любые символы за пределами закрывающегося тега выводятся автоматически: +?> +Hello World Again! +<?php + + +/************************************ + * Типы и Переменные + */ + +// Переменные начинаются с символа $. +// Правильное имя переменной начинается с буквы или знака подчеркивания, +// и может содержать любые цифры, буквы, или знаки подчеркивания. +// Не рекомендуется использовать кирилические символы в именах (прим. пер.) + +// Логические значения нечувствительны к регистру +$boolean = true; // или TRUE или True +$boolean = false; // или FALSE или False + +// Целые числа +$int1 = 12; // => 12 +$int2 = -12; // => -12- +$int3 = 012; // => 10 (ведущий 0 обозначает восьмеричное число) +$int4 = 0x0F; // => 15 (ведущие символы 0x означает шестнадцатеричное число) + +// Дробные числа +$float = 1.234; +$float = 1.2e3; +$float = 7E-10; + +// Арифметика +$sum = 1 + 1; // 2 +$difference = 2 - 1; // 1 +$product = 2 * 2; // 4 +$quotient = 2 / 1; // 2 + +// Арифметические сокращения +$number = 0; +$number += 1; // Увеличивает $number на 1 +echo $number++; // Печатает 1 (инкрементируется после вывода) +echo ++$number; // Печатает 3 (инкрементируется до вывода) +$number /= $float; // Делится и результат присваивается $number + +// Строки должны быть заключены в одинарные кавычки; +$sgl_quotes = '$String'; // => '$String' + +// Избегайте двойных кавычек за исключением случаев интерполирования переменной +$dbl_quotes = "This is a $sgl_quotes."; // => 'This is a $String.' + +// Специальные (escape) символы работают только в двойных кавычках +$escaped = "This contains a \t tab character."; +$unescaped = 'This just contains a slash and a t: \t'; + +// Заключайте переменные в фигурные скобки если это необходимо +$money = "I have $${number} in the bank."; + +// Начиная с PHP 5.3, синтаксис nowdocs может использоваться для +// неинтерполированного многострочного текста +$nowdoc = <<<'END' +Multi line +string +END; + +// Heredocs поддерживает интерполяцию переменных +$heredoc = <<<END +Multi line +$sgl_quotes +END; + +// Строки соединяются при помощи . +echo 'This string ' . 'is concatenated'; + + +/******************************** + * Константы + */ + +// Константа определяется при помощи define() +// и никогда не может быть изменена во время выполнения программы! + +// Правильное имя константы начинается с буквы или символа подчеркивания, +// и содержит любое колличество букв, цифр и знаков подчеркивания. +define("FOO", "something"); + +// Доступ к константе возможен через прямое указание её имени +echo 'This outputs '.FOO; + +/******************************** + * Массивы + */ + +// Все массивы в PHP - это ассоциативные массивы или хеши, + +// Ассоциативные массивы, известные в других языках как хеш-карты. + +// Работает во всех версиях РHP +$associative = array('One' => 1, 'Two' => 2, 'Three' => 3); + +// В PHP 5.4 появился новый синтаксис +$associative = ['One' => 1, 'Two' => 2, 'Three' => 3]; + +echo $associative['One']; // печатает 1 + +// Список тоже содержит целочисленные ключи +$array = ['One', 'Two', 'Three']; +echo $array[0]; // => "One" + + +/******************************** + * Вывод + */ + +echo('Hello World!'); +// Печатает Hello World! на stdout. +// Stdout это веб-страница запущенная в браузере. + +print('Hello World!'); // Аналогично echo + +// echo - это конструкция языка, вы можете опустить скобки. +echo 'Hello World!'; +print 'Hello World!'; // Выводит Hello World! + +$paragraph = 'paragraph'; + +echo 100; // Печать скалярной переменной напрямую +echo $paragraph; // или печать переменной + +// Если короткие теги разрешены, или ваша версия PHP >= 5.4 +// вы можете использовать сокращенный синтаксис echo +?> +<p><?= $paragraph ?></p> +<?php + +$x = 1; +$y = 2; +$x = $y; // $x теперь содержит значение переменной $y +$z = &$y; +// $z содержит ссылку на $y. Изменение значения $z затронет значение $y и наоборот. +// Значение $x остается неизменным. + +echo $x; // => 2 +echo $z; // => 2 +$y = 0; +echo $x; // => 2 +echo $z; // => 0 + + +/******************************** + * Логические выражения + */ +$a = 0; +$b = '0'; +$c = '1'; +$d = '1'; + +// Утверждение (assert) выдает предупреждение если аргумент не true + +// Эти сравнения всегда будут истинными, даже если типы будут различаться +assert($a == $b); // "равно" +assert($c != $a); // "не равно" +assert($c <> $a); // другое обозначение "не равно" +assert($a < $c); // меньше +assert($c > $b); // больше +assert($a <= $b); // меньше или равно +assert($c >= $d); // больше или равно + +// Следующие утверждения истинны если переменные имеют одинаковый тип. +assert($c === $d); +assert($a !== $d); +assert(1 == '1'); +assert(1 !== '1'); + +// Переменные могут изменять тип, в зависимости от их использования. +$integer = 1; +echo $integer + $integer; // => 2 + +$string = '1'; +echo $string + $string; // => 2 (строка превращается в число) + +// Выводится 0 по той причине, что оператор + не может привести строку 'one' к +// числовому типу +$string = 'one'; +echo $string + $string; // => 0 + +// Приведение типов (type casting) может быть использовано для преобразование +// переменной в другой тип +$boolean = (boolean) 1; // => true + +$zero = 0; +$boolean = (boolean) $zero; // => false + +// Также существуют функции выполняющие приведение типов +$integer = 5; +$string = strval($integer); +$float = floatval($integer); + +$var = null; // Null + +// И похожая по действию функция +$integer = 10; +$boolen = settype($integer, "string") // теперь $integer имеет строковый тип + +// settype возвращает true - если преобразование удалось и false в противном случае + +/******************************** + * Управляющие структуры + */ + +if (true) { + print 'I get printed'; +} + +if (false) { + print 'I don\'t'; +} else { + print 'I get printed'; +} + +if (false) { + print 'Does not get printed'; +} elseif(true) { + print 'Does'; +} + +// Тернарный оператор +print (false ? 'Does not get printed' : 'Does'); + +$x = 0; +if ($x === '0') { + print 'Does not print'; +} elseif($x == '1') { + print 'Does not print'; +} else { + print 'Does print'; +} + +// Альтернативный синтаксис полезный для шаблонов +?> + +<?php if ($x): ?> +This is displayed if the test is truthy. +<?php else: ?> +This is displayed otherwise. +<?php endif; ?> + +<?php + +// Использование switch. +switch ($x) { + case '0': + print 'Switch does type coercion'; + break; // You must include a break, or you will fall through + // to cases 'two' and 'three' + case 'two': + case 'three': + // Do something if $variable is either 'two' or 'three' + break; + default: + // Do something by default +} + +// Циклы: while, do...while и for +$i = 0; +while ($i < 5) { + echo $i++; +}; // Prints "01234" + +echo "\n"; + +$i = 0; +do { + echo $i++; +} while ($i < 5); // Prints "01234" + +echo "\n"; + +for ($x = 0; $x < 10; $x++) { + echo $x; +} // Prints "0123456789" + +echo "\n"; + +$wheels = ['bicycle' => 2, 'car' => 4]; + +// Циклы foreach могут обходить массивы +foreach ($wheels as $wheel_count) { + echo $wheel_count; +} // Prints "24" + +echo "\n"; + +// Вы можете обходить как ключи, так и их значения +foreach ($wheels as $vehicle => $wheel_count) { + echo "A $vehicle has $wheel_count wheels"; +} + +echo "\n"; + +$i = 0; +while ($i < 5) { + if ($i === 3) { + break; // Exit out of the while loop + } + echo $i++; +} // Prints "012" + +for ($i = 0; $i < 5; $i++) { + if ($i === 3) { + continue; // Skip this iteration of the loop + } + echo $i; +} // Prints "0124" + + +/******************************** + * Функции + */ + +// Определение функции: +function my_function () { + return 'Hello'; +} + +echo my_function(); // => "Hello" + +// Правильное имя функции начинается с буквы или символа подчеркивания +// и состоит из букв, цифр или знаков подчеркивания. + +function add ($x, $y = 1) { // $y по умолчанию равно 1 + $result = $x + $y; + return $result; +} + +echo add(4); // => 5 +echo add(4, 2); // => 6 + +// $result недоступна за пределами функции +// print $result; // Выдает предупреждение + +// Начиная с PHP 5.3 вы можете объявлять анонимные функции: +$inc = function ($x) { + return $x + 1; +}; + +echo $inc(2); // => 3 + +function foo ($x, $y, $z) { + echo "$x - $y - $z"; +} + +// Функции могут возвращать функции +function bar ($x, $y) { + // Используйте 'use' для передачи внешних переменных + return function ($z) use ($x, $y) { + foo($x, $y, $z); + }; +} + +$bar = bar('A', 'B'); +$bar('C'); // Prints "A - B - C" + +// Вы можете вызывать именованные функции используя строки +$function_name = 'add'; +echo $function_name(1, 2); // => 3 +// Полезно для программного определения запущенной функции. +// Или используйте call_user_func(callable $callback [, $parameter [, ... ]]); + + +/******************************** + * Includes + */ + +<?php +// PHP код внутри включаемого файла должен начинаться с тега PHP. + +include 'my-file.php'; +// Код в файле my-file.php теперь доступен в текущем в текущем пространстве имен. +// Если файл не удалось включить, будет выдано предупреждение. + +include_once 'my-file.php'; +// Если код в файле my-file.php уже был включен, он не будет включен повторно. +// Это предотвращает ошибку повторного включения файла. + +require 'my-file.php'; +require_once 'my-file.php'; + +// Same as include(), except require() will cause a fatal error if the +// file cannot be included. +// Действует также как и include(), но если файл не удалось подключить, +// функция выдает неисправимую ошибку + +// Содержимое файла my-include.php: +<?php + +return 'Anything you like.'; +// Конец файла + +// Эти функции могут также возвращать значения. +$value = include 'my-include.php'; + +// Имена файлов содержат их путь в файловой системе, или если передано просто +// имя файла, PHP обращается к директиве include_path. Если файл не найден в +// include_path, предпринимается попытка поиска в папке, где выполняется скрипт +// или в текущей рабочей директории. Если не в одном из этих мест файл не +// найден - выдается ошибка +/* */ + +/******************************** + * Классы + */ + +// Классы определяются при помощи ключевого слова "class" + +class MyClass +{ + const MY_CONST = 'value'; // A constant + + static $staticVar = 'static'; + + // Properties must declare their visibility + public $property = 'public'; + public $instanceProp; + protected $prot = 'protected'; // Accessible from the class and subclasses + private $priv = 'private'; // Accessible within the class only + + // Create a constructor with __construct + public function __construct($instanceProp) { + // Access instance variables with $this + $this->instanceProp = $instanceProp; + } + + // Methods are declared as functions inside a class + public function myMethod() + { + print 'MyClass'; + } + + final function youCannotOverrideMe() + { + } + + public static function myStaticMethod() + { + print 'I am static'; + } +} + +echo MyClass::MY_CONST; // Выведет 'value'; +echo MyClass::$staticVar; // Выведет 'static'; +MyClass::myStaticMethod(); // Выведет 'I am static'; + +// Новый экземпляр класса используя new +$my_class = new MyClass('An instance property'); + +// Если аргументы отсутствуют, можно не ставить круглые скобки + +// Доступ к членам класса используя -> +echo $my_class->property; // => "public" +echo $my_class->instanceProp; // => "An instance property" +$my_class->myMethod(); // => "MyClass" + +// Наследование классов используя "extends" +class MyOtherClass extends MyClass +{ + function printProtectedProperty() + { + echo $this->prot; + } + + // Override a method + function myMethod() + { + parent::myMethod(); + print ' > MyOtherClass'; + } +} + +$my_other_class = new MyOtherClass('Instance prop'); +$my_other_class->printProtectedProperty(); // => Выведет "protected" +$my_other_class->myMethod(); // Выведет "MyClass > MyOtherClass" + +final class YouCannotExtendMe +{ +} + +// Вы можете использовать "магические методы" для создания геттеров и сеттеров +class MyMapClass +{ + private $property; + + public function __get($key) + { + return $this->$key; + } + + public function __set($key, $value) + { + $this->$key = $value; + } +} + +$x = new MyMapClass(); +echo $x->property; // Будет использован метод __get() +$x->property = 'Something'; // Будет использован метод __set() + +// Классы могут быть абстрактными (используя ключевое слово abstract) +// или реализовывать интерфейсы (используя ключевое слово implements). +// Интерфейсы определяются при помощи ключевого слова interface + +interface InterfaceOne +{ + public function doSomething(); +} + +interface InterfaceTwo +{ + public function doSomethingElse(); +} + +// Интерфейсы могут быть расширены +interface InterfaceThree extends InterfaceTwo +{ + public function doAnotherContract(); +} + +abstract class MyAbstractClass implements InterfaceOne +{ + public $x = 'doSomething'; +} + +class MyConcreteClass extends MyAbstractClass implements InterfaceTwo +{ + public function doSomething() + { + echo $x; + } + + public function doSomethingElse() + { + echo 'doSomethingElse'; + } +} + +// Классы могут реализовывать более одного интерфейса +class SomeOtherClass implements InterfaceOne, InterfaceTwo +{ + public function doSomething() + { + echo 'doSomething'; + } + + public function doSomethingElse() + { + echo 'doSomethingElse'; + } +} + + +/******************************** + * Трейты + */ + +// Трейты появились в PHP 5.4.0 и объявляются при помощи ключевого слова trait + +trait MyTrait +{ + public function myTraitMethod() + { + print 'I have MyTrait'; + } +} + +class MyTraitfulClass +{ + use MyTrait; +} + +$cls = new MyTraitfulClass(); +$cls->myTraitMethod(); // Prints "I have MyTrait" + + +/******************************** + * Пространства имен + */ + +// Это секция особая, ведь объявление пространства имен +// должно быть самым первым в файле. Позвольте сделать вид, что это не так + +<?php + +// По умолчанию, классы существуют в глобальном пространстве имен и могут быть +// вызваны с обратным слешем. + +$cls = new \MyClass(); + +// Задание пространства имен файла +namespace My\Namespace; + +class MyClass +{ +} + +// (из другого файла) +$cls = new My\Namespace\MyClass; + +// Или внутри другого пространства имен. +namespace My\Other\Namespace; + +use My\Namespace\MyClass; + +$cls = new MyClass(); + +// Или вы можете создать псевдоним для пространства имен: +namespace My\Other\Namespace; + +use My\Namespace as SomeOtherNamespace; + +$cls = new SomeOtherNamespace\MyClass(); + +*/ + +``` + +## Смотрите также: +Посетите страницу [официальной документации PHP](http://www.php.net/manual/) для справки. +Если вас интересуют полезные приемы использования PHP посетите [PHP The Right Way](http://www.phptherightway.com/). +Если вы раньше пользовались языком с хорошей организацией пакетов, посмотрите [Composer](http://getcomposer.org/). +Для изучения стандартов использования языка посетите PHP Framework Interoperability Group's [PSR standards](https://github.com/php-fig/fig-standards). diff --git a/ru-ru/python-ru.html.markdown b/ru-ru/python-ru.html.markdown new file mode 100644 index 00000000..204eb357 --- /dev/null +++ b/ru-ru/python-ru.html.markdown @@ -0,0 +1,493 @@ +--- +language: python +lang: ru-ru +contributors: + - ["Louie Dinh", "http://ldinh.ca"] +translators: + - ["Yury Timofeev", "http://twitter.com/gagar1n"] +filename: learnpython-ru.py +--- + +Язык Python был создан Гвидо ван Россумом в начале 90-х. Сейчас это один из самых популярных +языков. Я люблю его за его понятный и доходчивый синтаксис - это почти что исполняемый псевдокод. + +С благодарностью жду ваших отзывов: [@louiedinh](http://twitter.com/louiedinh) или louiedinh [at] [google's email service] + +Замечание: Эта статья относится к Python 2.7, но должно работать и в Python 2.x. Скоро будет версия и для Python 3! + +```python +# Однострочные комментарии начинаются с hash-символа. +""" Многострочный текст может быть + записан, используя 3 знака " и обычно используется + в качестве комментария +""" + +#################################################### +## 1. Примитивные типы данных и операторов +#################################################### + +# У вас есть числа +3 #=> 3 + +# Математика работает вполне ожидаемо +1 + 1 #=> 2 +8 - 1 #=> 7 +10 * 2 #=> 20 +35 / 5 #=> 7 + +# А вот деление немного сложнее. В этом случае происходит деление +# целых чисел и результат автоматически округляется в меньшую сторону. +5 / 2 #=> 2 + +# Чтобы научиться делить, сначала нужно немного узнать о дробных числах. +2.0 # Это дробное число +11.0 / 4.0 #=> 2.75 Вооот... Так гораздо лучше + +# Приоритет операций указывается скобками +(1 + 3) * 2 #=> 8 + +# Логические значения являются примитивами +True +False + +# Для отрицания используется ключевое слово not +not True #=> False +not False #=> True + +# Равенство это == +1 == 1 #=> True +2 == 1 #=> False + +# Неравенство это != +1 != 1 #=> False +2 != 1 #=> True + +# Еще немного сравнений +1 < 10 #=> True +1 > 10 #=> False +2 <= 2 #=> True +2 >= 2 #=> True + +# Сравнения могут быть соединены в цепь! +1 < 2 < 3 #=> True +2 < 3 < 2 #=> False + +# Строки определяются символом " или ' +"Это строка." +'Это тоже строка.' + +# И строки тоже могут складываться! +"Привет " + "мир!" #=> "Привет мир!" + +# Со строкой можно работать, как со списком символов +"Это строка"[0] #=> 'Э' + +# Символ % используется для форматирования строк, например: +"%s могут быть %s" % ("строки", "интерполированы") + +# Новый метод форматирования строк - использование метода format. +# Это предпочитаемый способ. +"{0} могут быть {1}".format("строки", "форматированы") +# Если вы не хотите считать, можете использовать ключевые слова. +"{name} хочет есть {food}".format(name="Боб", food="лазанью") + +# None является объектом +None #=> None + +# Не используйте оператор равенства '=='' для сравнения +# объектов с None. Используйте для этого 'is' +"etc" is None #=> False +None is None #=> True + +# Оператор 'is' проверяет идентичность объектов. Он не +# очень полезен при работе с примитивными типами, но +# зато просто незаменим при работе с объектами. + +# None, 0, и пустые строки/списки равны False. +# Все остальные значения равны True +0 == False #=> True +"" == False #=> True + + +#################################################### +## 2. Переменные и коллекции +#################################################### + +# Печатать довольно просто +print "Я Python. Приятно познакомиться!" + + +# Необязательно объявлять переменные перед их инициализацией. +some_var = 5 # По соглашению используется нижний_регистр_с_подчеркиваниями +some_var #=> 5 + +# При попытке доступа к неинициализированной переменной, +# выбрасывается исключение. +# См. раздел "Поток управления" для информации об исключениях. +some_other_var # Выбрасывает ошибку именования + +# if может быть использован как выражение +"yahoo!" if 3 > 2 else 2 #=> "yahoo!" + +# Списки хранят последовательности +li = [] +# Можно сразу начать с заполненным списком +other_li = [4, 5, 6] + +# Объекты добавляются в конец списка методом append +li.append(1) # [1] +li.append(2) # [1, 2] +li.append(4) # [1, 2, 4] +li.append(3) # [1, 2, 4, 3] +# И удаляются с конца методом pop +li.pop() #=> возвращает 3 и li становится равен [1, 2, 4] +# Положим элемент обратно +li.append(3) # [1, 2, 4, 3]. + +# Обращайтесь со списком, как с обычным массивом +li[0] #=> 1 +# Обратимся к последнему элементу +li[-1] #=> 3 + +# Попытка выйти за границы массива приведет к IndexError +li[4] # Выдает IndexError + +# Можно обращаться к диапазону, используя "кусочный синтаксис" (slice syntax) +# (Для тех, кто любит математику, это называется замкнуто/открытый интервал.) +li[1:3] #=> [2, 4] +# Опускаем начало +li[2:] #=> [4, 3] +# Опускаем конец +li[:3] #=> [1, 2, 4] + +# Удаляем произвольные элементы из списка оператором del +del li[2] # [1, 2, 3] + +# Вы можете складывать списки +li + other_li #=> [1, 2, 3, 4, 5, 6] - Замечание: li и other_li остаются нетронутыми + +# Конкатенировать списки можно методом extend +li.extend(other_li) # Теперь li содержит [1, 2, 3, 4, 5, 6] + +# Проверить элемент на вхождение в список можно оператором in +1 in li #=> True + +# Длина списка вычисляется функцией len +len(li) #=> 6 + + +# Кортежи - это такие списки, только неизменяемые +tup = (1, 2, 3) +tup[0] #=> 1 +tup[0] = 3 # Выдает TypeError + +# Все то же самое можно делать и с кортежами +len(tup) #=> 3 +tup + (4, 5, 6) #=> (1, 2, 3, 4, 5, 6) +tup[:2] #=> (1, 2) +2 in tup #=> True + +# Вы можете распаковывать кортежи (или списки) в переменные +a, b, c = (1, 2, 3) # a == 1, b == 2 и c == 3 +# Кортежи создаются по умолчанию, если опущены скобки +d, e, f = 4, 5, 6 +# Обратите внимание, как легко поменять местами значения двух переменных +e, d = d, e # теперь d == 5, а e == 4 + + +# Словари содержат ассоциативные массивы +empty_dict = {} +# Вот так описывается предзаполненный словарь +filled_dict = {"one": 1, "two": 2, "three": 3} + +# Значения ищутся по ключу с помощью оператора [] +filled_dict["one"] #=> 1 + +# Можно получить все ключи в виде списка +filled_dict.keys() #=> ["three", "two", "one"] +# Замечание - сохранение порядка ключей в словаре не гарантируется +# Ваши результаты могут не совпадать с этими. + +# Можно получить и все значения в виде списка +filled_dict.values() #=> [3, 2, 1] +# То же самое замечание насчет порядка ключей справедливо и здесь + +# При помощи оператора in можно проверять ключи на вхождение в словарь +"one" in filled_dict #=> True +1 in filled_dict #=> False + +# Попытка получить значение по несуществующему ключу выбросит KeyError +filled_dict["four"] # KeyError + +# Чтобы избежать этого, используйте метод get +filled_dict.get("one") #=> 1 +filled_dict.get("four") #=> None +# Метод get также принимает аргумент default, значение которого будет +# возвращено при отсутствии указанного ключа +filled_dict.get("one", 4) #=> 1 +filled_dict.get("four", 4) #=> 4 + +# Метод setdefault - это безопасный способ добавить новую пару ключ-значение в словарь +filled_dict.setdefault("five", 5) #filled_dict["five"] возвращает 5 +filled_dict.setdefault("five", 6) #filled_dict["five"] по прежнему возвращает 5 + + +# Множества содержат... ну, в общем, множества +empty_set = set() +# Инициализация множества набором значений +some_set = set([1,2,2,3,4]) # some_set теперь равно set([1, 2, 3, 4]) + +# Начиная с Python 2.7, вы можете использовать {} чтобы обьявить множество +filled_set = {1, 2, 2, 3, 4} # => {1 2 3 4} + +# Добавление новых элементов в множество +filled_set.add(5) # filled_set равно {1, 2, 3, 4, 5} + +# Пересечение множеств: & +other_set = {3, 4, 5, 6} +filled_set & other_set #=> {3, 4, 5} + +# Объединение множеств: | +filled_set | other_set #=> {1, 2, 3, 4, 5, 6} + +# Разность множеств: - +{1,2,3,4} - {2,3,5} #=> {1, 4} + +# Проверка на вхождение во множество: in +2 in filled_set #=> True +10 in filled_set #=> False + + +#################################################### +## 3. Поток управления +#################################################### + +# Для начала заведем переменную +some_var = 5 + +# Так выглядит выражение if. Отступы в python очень важны! +# результат: "some_var меньше, чем 10" +if some_var > 10: + print "some_var намного больше, чем 10." +elif some_var < 10: # Выражение elif необязательно. + print "some_var меньше, чем 10." +else: # Это тоже необязательно. + print "some_var равно 10." + + +""" +Циклы For проходят по спискам + +Результат: + собака это млекопитающее + кошка это млекопитающее + мышь это млекопитающее +""" +for animal in ["собака", "кошка", "мышь"]: + # Можете использовать оператор % для интерполяции форматированных строк + print "%s это млекопитающее" % animal + +""" +`range(number)` возвращает список чисел +от нуля до заданного числа +Результат: + 0 + 1 + 2 + 3 +""" +for i in range(4): + print i + +""" +Циклы while продолжаются до тех пор, пока указанное условие не станет ложным. +Результат: + 0 + 1 + 2 + 3 +""" +x = 0 +while x < 4: + print x + x += 1 # То же самое, что x = x + 1 + +# Обрабывайте исключения блоками try/except + +# Работает в Python 2.6 и выше: +try: + # Для выбора ошибки используется raise + raise IndexError("Это IndexError") +except IndexError as e: + # pass это просто отсутствие оператора. Обычно здесь происходит + # восстановление от ошибки. + pass + + +#################################################### +## 4. Функции +#################################################### + +# Используйте def для создания новых функций +def add(x, y): + print "x равен %s, а y равен %s" % (x, y) + return x + y # Возвращайте результат выражением return + +# Вызов функции с аргументами +add(5, 6) #=> prints out "x равен 5, а y равен 6" и возвращает 11 + +# Другой способ вызова функции с аргументами +add(y=6, x=5) # Именованные аргументы можно указывать в любом порядке. + +# Вы можете определить функцию, принимающую неизвестное количество аргументов +def varargs(*args): + return args + +varargs(1, 2, 3) #=> (1,2,3) + + +# А также можете определить функцию, принимающую изменяющееся количество +# именованных аргументов +def keyword_args(**kwargs): + return kwargs + +# Вызовем эту функцию и посмотрим, что из этого получится +keyword_args(big="foot", loch="ness") #=> {"big": "foot", "loch": "ness"} + +# Если хотите, можете использовать оба способа одновременно +def all_the_args(*args, **kwargs): + print args + print kwargs +""" +all_the_args(1, 2, a=3, b=4) выводит: + (1, 2) + {"a": 3, "b": 4} +""" + +# Вызывая функции, можете сделать наоборот! +# Используйте символ * для передачи кортежей и ** для передачи словарей +args = (1, 2, 3, 4) +kwargs = {"a": 3, "b": 4} +all_the_args(*args) # эквивалент foo(1, 2, 3, 4) +all_the_args(**kwargs) # эквивалент foo(a=3, b=4) +all_the_args(*args, **kwargs) # эквивалент foo(1, 2, 3, 4, a=3, b=4) + +# Python имеет функции первого класса +def create_adder(x): + def adder(y): + return x + y + return adder + +add_10 = create_adder(10) +add_10(3) #=> 13 + +# Также есть и анонимные функции +(lambda x: x > 2)(3) #=> True + +# Есть встроенные функции высшего порядка +map(add_10, [1,2,3]) #=> [11, 12, 13] +filter(lambda x: x > 5, [3, 4, 5, 6, 7]) #=> [6, 7] + +# Мы можем использовать списки для удобного отображения и фильтрации +[add_10(i) for i in [1, 2, 3]] #=> [11, 12, 13] +[x for x in [3, 4, 5, 6, 7] if x > 5] #=> [6, 7] + +#################################################### +## 5. Классы +#################################################### + +# Чтобы получить класс, мы наследуемся от object. +class Human(object): + + # Атрибут класса. Он разделяется всеми экземплярами этого класса + species = "H. sapiens" + + # Обычный конструктор + def __init__(self, name): + # Присваивание значения аргумента атрибуту класса name + self.name = name + + # Метод экземпляра. Все методы принимают self в качестве первого аргумента + def say(self, msg): + return "%s: %s" % (self.name, msg) + + # Метод класса разделяется между всеми экземплярами + # Они вызываются с указыванием вызывающего класса в качестве первого аргумента + @classmethod + def get_species(cls): + return cls.species + + # Статический метод вызывается без ссылки на класс или экземпляр + @staticmethod + def grunt(): + return "*grunt*" + + +# Инстанцирование класса +i = Human(name="Иван") +print i.say("привет") # "Иван: привет" + +j = Human("Петр") +print j.say("Привет") # "Петр: привет" + +# Вызов метода класса +i.get_species() #=> "H. sapiens" + +# Присвоение разделяемому атрибуту +Human.species = "H. neanderthalensis" +i.get_species() #=> "H. neanderthalensis" +j.get_species() #=> "H. neanderthalensis" + +# Вызов статического метода +Human.grunt() #=> "*grunt*" + + +#################################################### +## 6. Модули +#################################################### + +# Вы можете импортировать модули +import math +print math.sqrt(16) #=> 4 + +# Вы можете импортировать отдельные функции модуля +from math import ceil, floor +print ceil(3.7) #=> 4.0 +print floor(3.7) #=> 3.0 + +# Можете импортировать все функции модуля. +# (Хотя это и не рекомендуется) +from math import * + +# Можете сокращать имена модулей +import math as m +math.sqrt(16) == m.sqrt(16) #=> True + +# Модули в Python это обычные файлы с кодом python. Вы +# можете писать свои модули и импортировать их. Название +# модуля совпадает с названием файла. + +# Вы можете узнать, какие функции и атрибуты определены +# в модуле +import math +dir(math) + + +``` + +## Хотите еще? + +### Бесплатные онлайн-материалы + +* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/) +* [Dive Into Python](http://www.diveintopython.net/) +* [The Official Docs](http://docs.python.org/2.6/) +* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/) +* [Python Module of the Week](http://pymotw.com/2/) + +### Платные + +* [Programming Python](http://www.amazon.com/gp/product/0596158106/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596158106&linkCode=as2&tag=homebits04-20) +* [Dive Into Python](http://www.amazon.com/gp/product/1441413022/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441413022&linkCode=as2&tag=homebits04-20) +* [Python Essential Reference](http://www.amazon.com/gp/product/0672329786/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0672329786&linkCode=as2&tag=homebits04-20) + diff --git a/ru-ru/ruby-ru.html.markdown b/ru-ru/ruby-ru.html.markdown new file mode 100644 index 00000000..318e0e09 --- /dev/null +++ b/ru-ru/ruby-ru.html.markdown @@ -0,0 +1,468 @@ +--- +language: ruby +lang: ru-ru +filename: learnruby-ru.rb +contributors: + - ["David Underwood", "http://theflyingdeveloper.com"] + - ["Joel Walden", "http://joelwalden.net"] + - ["Luke Holder", "http://twitter.com/lukeholder"] + - ["Tristan Hume", "http://thume.ca/"] + - ["Nick LaMuro", "https://github.com/NickLaMuro"] +translators: + - ["Alexey Makarov", "https://github.com/Anakros"] +--- + +```ruby +# Это комментарий + +=begin +Это многострочный комментарий +Никто их не использует +И они не рекомендуются к использованию +=end + +# Первое и самое главное: Всё является объектом. + +# Числа это объекты + +3.class #=> Fixnum + +3.to_s #=> "3" + + +# Немного простой арифметики +1 + 1 #=> 2 +8 - 1 #=> 7 +10 * 2 #=> 20 +35 / 5 #=> 7 + +# Арифметика -- это синтаксический сахар +# над вызовом метода для объекта +1.+(3) #=> 4 +10.* 5 #=> 50 + +# Логические величины -- это объекты +nil # Здесь ничего нет +true # истина +false # ложь + +nil.class #=> NilClass +true.class #=> TrueClass +false.class #=> FalseClass + +# Операция равенства +1 == 1 #=> true +2 == 1 #=> false + +# Операция неравенства +1 != 1 #=> false +2 != 1 #=> true +!true #=> false +!false #=> true + +# nil -- имеет такое же логическое значение, как и false + +!nil #=> true +!false #=> true +!0 #=> false + +# Больше операций сравнения +1 < 10 #=> true +1 > 10 #=> false +2 <= 2 #=> true +2 >= 2 #=> true + +# Строки -- это объекты + +'Я строка'.class #=> String +"Я тоже строка".class #=> String + +placeholder = "использовать интерполяцию строк" +"Я могу #{placeholder}, когда создаю строку с двойными кавычками" +#=> "Я могу использовать интерполяцию строк, +# когда создаю строку с двойными кавычками" + + +# печатать в стандартный вывод +puts "Я печатаюсь!" + +# Переменные +x = 25 #=> 25 +x #=> 25 + +# Присваивание значения возвращает то самое присвоенное значение. +# Это позволяет делать множественные присваивания: + +x = y = 10 #=> 10 +x #=> 10 +y #=> 10 + +# По соглашению, используйте snake_case для имён переменных +snake_case = true + +# Используйте подробные имена для переменных +# Но не переборщите! +path_to_project_root = '/good/name/' +path = '/bad/name/' + +# Идентификаторы (тоже объекты) + +# Идентификаторы -- это неизменяемые, многоразовые константы. +# Для каждого идентификатора (кроме текста) сохраняется цифровой хэш. +# При последующем использовании идентификатора, заместо создания нового объекта, +# будет найден уже существующий по цифровому хэшу. +# Они часто используются вместо строк для ускорения работы приложений + +:pending.class #=> Symbol + +status = :pending + +status == :pending #=> true + +status == 'pending' #=> false + +status == :approved #=> false + +# Массивы + +# Это массив +array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] + +# Массив может содержать различные типы значений + +[1, "hello", false] #=> [1, "hello", false] + +# Значение в массиве можно получить по индексу с левой границы +array[0] #=> 1 +array[12] #=> nil + +# Как и арифметика, доступ к значению в массиве +# это синтаксический сахар над вызовом метода для объекта +array.[] 0 #=> 1 +array.[] 12 #=> nil + +# Также, можно получить по индексу с правой границы +array[-1] #=> 5 + +# С заданными левой и правой границами индексов +array[2, 4] #=> [3, 4, 5] + +# Или с использованием диапазона значений +array[1..3] #=> [2, 3, 4] + +# Вот так можно добавить значение в массив +array << 6 #=> [1, 2, 3, 4, 5, 6] + +# Хэши -- это массив пар "ключ => значение". +# Хэши объявляются с использованием фигурных скобок: +hash = {'color' => 'green', 'number' => 5} + +hash.keys #=> ['color', 'number'] + +# Значение в хэше легко может быть найдено по ключу: +hash['color'] #=> 'green' +hash['number'] #=> 5 + +# Поиск по ключу, которого в хэше нет вернёт nil: +hash['nothing here'] #=> nil + +# начиная с Ruby 1.9, существует специальный синтаксис +# при использовании идентификаторов как ключей хэша: + +new_hash = { defcon: 3, action: true} + +new_hash.keys #=> [:defcon, :action] + +# Массивы и Хэши -- перечисляемые типы данных +# У них есть много полезных методов, например: each, map, count и другие + +# Управление ходом выполнения (Управляющие структуры) + +if true + "Если истина" +elsif false + "Иначе, если ложь (опционально)" +else + "Во всех других случаях" +end + +for counter in 1..5 + puts "итерация #{counter}" +end +#=> итерация 1 +#=> итерация 2 +#=> итерация 3 +#=> итерация 4 +#=> итерация 5 + +# Однако, никто не использует "for" для циклов. +# Вместо него Вы должны использовать метод "each" вместе с блоком кода. +# +# Блок кода -- это один из вариантов создания замыканий (лямбды, +# анонимные функции). +# Блок может только передаваться методу, сам по себе он существовать не может. +# "for" не имеет своей области видимости и все переменные, объявленные в нём +# будут доступны отовсюду. "each" вместе с блоком создаёт свою область видимости + +# Метод "each" для диапазона значений запускает блок кода один раз +# для каждого из значений диапазона +# Блок передаёт счётчик (counter) в качестве параметра. +# Вызов метода "each" с блоком выглядит следующим образом: + +(1..5).each do |counter| + puts "итерация #{counter}" +end +#=> итерация 1 +#=> итерация 2 +#=> итерация 3 +#=> итерация 4 +#=> итерация 5 + +# Вы также можете ограничивать блоки фигурными скобками: +(1..5).each {|counter| puts "итерация #{counter}"} + +# Содержимое структурных данных также можно перебирать используя "each": +array.each do |element| + puts "#{element} -- часть массива" +end +hash.each do |key, value| + puts "#{key} -- это #{value}" +end + +counter = 1 +while counter <= 5 do + puts "итерация #{counter}" + counter += 1 +end +#=> итерация 1 +#=> итерация 2 +#=> итерация 3 +#=> итерация 4 +#=> итерация 5 + +grade = 'B' + +case grade +when 'A' + puts "Так держать, детка!" +when 'B' + puts "Тебе повезёт в следующий раз" +when 'C' + puts "Ты можешь сделать лучше" +when 'D' + puts "Выскоблил последнее" +when 'F' + puts "Ты провалился!" +else + puts "Альтернативная система оценок, да?" +end + +# Функции + +def double(x) + x * 2 +end + +# Функции (и все блоки) неявно возвращают значение последней операции +double(2) #=> 4 + +# Скобки необязательны, если возвращаемый результат однозначен +double 3 #=> 6 + +double double 3 #=> 12 + +def sum(x,y) + x + y +end + +# Аргументы метода разделены запятой +sum 3, 4 #=> 7 + +sum sum(3,4), 5 #=> 12 + +# yield +# Все методы имеют неявный, опциональный параметр, +# который может быть вызван с помощью инструкции "yield" + +def surround + puts "{" + yield + puts "}" +end + +surround { puts 'hello world' } + +# { +# hello world +# } + + +# Определение класса с помощью ключевого слова "class" +class Human + + # Переменная класса, она является общей для всех экземпляров класса + @@species = "H. sapiens" + + # Базовый метод-конструктор + def initialize(name, age=0) + # Присвоить аргумент "name" переменной "name" экземпляра класса + @name = name + # Если аргумент "age" не задан, + # мы используем значение по умолчанию из списка аргументов + @age = age + end + + # Базовый метод установки значения для переменной (setter) + def name=(name) + @name = name + end + + # Базовый метод получения значения переменной (getter) + def name + @name + end + + # Метод класса определяется с ключевым словом "self", + # чтобы можно было отличить его от метода экземпляра класса. + # Он может быть вызван только на уровне класса, но не экземпляра. + def self.say(msg) + puts "#{msg}" + end + + def species + @@species + end + +end + + +# Создание экземпляра класса +jim = Human.new("Jim Halpert") + +dwight = Human.new("Dwight K. Schrute") + +# Давайте вызовем несколько методов +jim.species #=> "H. sapiens" +jim.name #=> "Jim Halpert" +jim.name = "Jim Halpert II" #=> "Jim Halpert II" +jim.name #=> "Jim Halpert II" +dwight.species #=> "H. sapiens" +dwight.name #=> "Dwight K. Schrute" + +# Вызов метода класса +Human.say("Hi") #=> "Hi" + +# Область видимости переменной определяется тем, как мы даём имя переменной. +# Переменные, имя которых начинается с "$" имеют глобальную область видимости +$var = "I'm a global var" +defined? $var #=> "global-variable" + +# Переменная экземпляра класса, она видна только в экземпляре +@var = "I'm an instance var" +defined? @var #=> "instance-variable" + +# Переменная класса, видна для всех экземпляров этого класса и в самом классе +@@var = "I'm a class var" +defined? @@var #=> "class variable" + +# Имена переменных с большой буквы используются для создания констант +Var = "I'm a constant" +defined? Var #=> "constant" + +# Класс тоже объект в Ruby. Класс может иметь переменные экземпляра. +# Переменная класса доступна в классе, его экземплярах и его потомках. + +# Пример класса +class Human + @@foo = 0 + + def self.foo + @@foo + end + + def self.foo=(value) + @@foo = value + end +end + +# Производный класс (класс-потомок) +class Worker < Human +end + +Human.foo # 0 +Worker.foo # 0 + +Human.foo = 2 # 2 +Worker.foo # 2 + +# Переменная экземпляра класса недоступна в потомках этого класса. + +class Human + @bar = 0 + + def self.bar + @bar + end + + def self.bar=(value) + @bar = value + end +end + +class Doctor < Human +end + +Human.bar # 0 +Doctor.bar # nil + +module ModuleExample + def foo + 'foo' + end +end + +# Включение модулей в класс добавляет их методы в экземпляр класса +# Или в сам класс, зависит только от метода подключения +class Person + include ModuleExample +end + +class Book + extend ModuleExample +end + +Person.foo # => NoMethodError: undefined method `foo' for Person:Class +Person.new.foo # => 'foo' +Book.foo # => 'foo' +Book.new.foo # => NoMethodError: undefined method `foo' + +# Коллбэки при подключении модуля + +module ConcernExample + def self.included(base) + base.extend(ClassMethods) + base.send(:include, InstanceMethods) + end + + module ClassMethods + def bar + 'bar' + end + end + + module InstanceMethods + def qux + 'qux' + end + end +end + +class Something + include ConcernExample +end + +Something.bar # => 'bar' +Something.qux # => NoMethodError: undefined method `qux' +Something.new.bar # => NoMethodError: undefined method `bar' +Something.new.qux # => 'qux' +``` |