summaryrefslogtreecommitdiffhomepage
path: root/ru-ru/elixir-ru.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'ru-ru/elixir-ru.html.markdown')
-rw-r--r--ru-ru/elixir-ru.html.markdown467
1 files changed, 467 insertions, 0 deletions
diff --git a/ru-ru/elixir-ru.html.markdown b/ru-ru/elixir-ru.html.markdown
new file mode 100644
index 00000000..c8c2c060
--- /dev/null
+++ b/ru-ru/elixir-ru.html.markdown
@@ -0,0 +1,467 @@
+---
+language: elixir
+contributors:
+ - ["Joao Marques", "http://github.com/mrshankly"]
+ - ["Dzianis Dashkevich", "https://github.com/dskecse"]
+ - ["Ryan Plant", "https://github.com/ryanplant-au"]
+translator:
+ - ["Ev Bogdanov", "https://github.com/evbogdanov"]
+filename: learnelixir-ru.ex
+lang: ru-ru
+---
+
+Elixir — современный функциональный язык программирования, который работает на
+виртуальной машине Erlang. Elixir полностью совместим с Erlang, но обладает
+дружелюбным синтаксисом и предлагает больше возможностей.
+
+```elixir
+
+# Однострочные комментарии начинаются с символа решётки.
+
+# Для многострочных комментариев отдельного синтаксиса нет,
+# поэтому просто используйте несколько однострочных комментариев.
+
+# Запустить интерактивную Elixir-консоль (аналог `irb` в Ruby) можно
+# при помощи команды `iex`.
+# Чтобы скомпилировать модуль, воспользуйтесь командой `elixirc`.
+
+# Обе команды будут работать из терминала, если вы правильно установили Elixir.
+
+## ---------------------------
+## -- Базовые типы
+## ---------------------------
+
+# Числа
+3 # целое число
+0x1F # целое число
+3.0 # число с плавающей запятой
+
+# Атомы, которые являются нечисловыми константами. Они начинаются с символа `:`.
+:hello # атом
+
+# Кортежи, которые хранятся в памяти последовательно.
+{1,2,3} # кортеж
+
+# Получить доступ к элементу кортежа мы можем с помощью функции `elem`:
+elem({1, 2, 3}, 0) #=> 1
+
+# Списки, которые реализованы как связные списки.
+[1,2,3] # список
+
+# У каждого непустого списка есть голова (первый элемент списка)
+# и хвост (все остальные элементы списка):
+[head | tail] = [1,2,3]
+head #=> 1
+tail #=> [2,3]
+
+# В Elixir, как и в Erlang, знак `=` служит для сопоставления с образцом,
+# а не для операции присваивания.
+#
+# Это означает, что выражение слева от знака `=` (образец) сопоставляется с
+# выражением справа.
+#
+# Сопоставление с образцом позволило нам получить голову и хвост списка
+# в примере выше.
+
+# Если выражения слева и справа от знака `=` не удаётся сопоставить, будет
+# брошена ошибка. Например, если кортежи разных размеров.
+{a, b, c} = {1, 2} #=> ** (MatchError)
+
+# Бинарные данные
+<<1,2,3>>
+
+# Вы столкнётесь с двумя видами строк:
+"hello" # Elixir-строка (заключена в двойные кавычки)
+'hello' # Erlang-строка (заключена в одинарные кавычки)
+
+# Все строки представлены в кодировке UTF-8:
+"привет" #=> "привет"
+
+# Многострочный текст
+"""
+Я текст на несколько
+строк.
+"""
+#=> "Я текст на несколько\nстрок.\n"
+
+# Чем Elixir-строки отличаются от Erlang-строк? Elixir-строки являются бинарными
+# данными.
+<<?a, ?b, ?c>> #=> "abc"
+# Erlang-строка — это на самом деле список.
+[?a, ?b, ?c] #=> 'abc'
+
+# Оператор `?` возвращает целое число, соответствующее данному символу.
+?a #=> 97
+
+# Для объединения бинарных данных (и Elixir-строк) используйте `<>`
+<<1,2,3>> <> <<4,5>> #=> <<1,2,3,4,5>>
+"hello " <> "world" #=> "hello world"
+
+# Для объединения списков (и Erlang-строк) используйте `++`
+[1,2,3] ++ [4,5] #=> [1,2,3,4,5]
+'hello ' ++ 'world' #=> 'hello world'
+
+# Диапазоны записываются как `начало..конец` (оба включительно)
+1..10 #=> 1..10
+
+# Сопоставление с образцом применимо и для диапазонов:
+lower..upper = 1..10
+[lower, upper] #=> [1, 10]
+
+# Карты (известны вам по другим языкам как ассоциативные массивы, словари, хэши)
+genders = %{"david" => "male", "gillian" => "female"}
+genders["david"] #=> "male"
+
+# Для карт, где ключами выступают атомы, доступен специальный синтаксис
+genders = %{david: "male", gillian: "female"}
+genders.gillian #=> "female"
+
+## ---------------------------
+## -- Операторы
+## ---------------------------
+
+# Математические операции
+1 + 1 #=> 2
+10 - 5 #=> 5
+5 * 2 #=> 10
+10 / 2 #=> 5.0
+
+# В Elixir оператор `/` всегда возвращает число с плавающей запятой.
+
+# Для целочисленного деления применяйте `div`
+div(10, 2) #=> 5
+
+# Для получения остатка от деления к вашим услугам `rem`
+rem(10, 3) #=> 1
+
+# Булевые операторы: `or`, `and`, `not`.
+# В качестве первого аргумента эти операторы ожидают булевое значение.
+true and true #=> true
+false or true #=> true
+1 and true #=> ** (BadBooleanError)
+
+# Elixir также предоставляет `||`, `&&` и `!`, которые принимают аргументы
+# любого типа. Всё, кроме `false` и `nil`, считается `true`.
+1 || true #=> 1
+false && 1 #=> false
+nil && 20 #=> nil
+!true #=> false
+
+# Операторы сравнения: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<`, `>`
+1 == 1 #=> true
+1 != 1 #=> false
+1 < 2 #=> true
+
+# Операторы `===` и `!==` более строгие. Разница заметна, когда мы сравниваем
+# числа целые и с плавающей запятой:
+1 == 1.0 #=> true
+1 === 1.0 #=> false
+
+# Elixir позволяет сравнивать значения разных типов:
+1 < :hello #=> true
+
+# При сравнении разных типов руководствуйтесь следующим правилом:
+# число < атом < ссылка < функция < порт < процесс < кортеж < список < строка
+
+## ---------------------------
+## -- Порядок выполнения
+## ---------------------------
+
+# Условный оператор `if`
+if false do
+ "Вы этого никогда не увидите"
+else
+ "Вы увидите это"
+end
+
+# Противоположный ему условный оператор `unless`
+unless true do
+ "Вы этого никогда не увидите"
+else
+ "Вы увидите это"
+end
+
+# Помните сопоставление с образцом?
+# Многие конструкции в Elixir построены вокруг него.
+
+# `case` позволяет сравнить выражение с несколькими образцами:
+case {:one, :two} do
+ {:four, :five} ->
+ "Этот образец не совпадёт"
+ {:one, x} ->
+ "Этот образец совпадёт и присвоит переменной `x` значение `:two`"
+ _ ->
+ "Этот образец совпадёт с чем угодно"
+end
+
+# Символ `_` называется анонимной переменной. Используйте `_` для значений,
+# которые в текущем выражении вас не интересуют. Например, вам интересна лишь
+# голова списка, а хвост вы желаете проигнорировать:
+[head | _] = [1,2,3]
+head #=> 1
+
+# Для лучшей читаемости вы можете написать:
+[head | _tail] = [:a, :b, :c]
+head #=> :a
+
+# `cond` позволяет проверить сразу несколько условий за раз.
+# Используйте `cond` вместо множественных операторов `if`.
+cond do
+ 1 + 1 == 3 ->
+ "Вы меня никогда не увидите"
+ 2 * 5 == 12 ->
+ "И меня"
+ 1 + 2 == 3 ->
+ "Вы увидите меня"
+end
+
+# Обычно последним условием идёт `true`, которое выполнится, если все предыдущие
+# условия оказались ложны.
+cond do
+ 1 + 1 == 3 ->
+ "Вы меня никогда не увидите"
+ 2 * 5 == 12 ->
+ "И меня"
+ true ->
+ "Вы увидите меня (по сути, это `else`)"
+end
+
+# Обработка ошибок происходит в блоках `try/catch`.
+# Elixir также поддерживает блок `after`, который выполнится в любом случае.
+try do
+ throw(:hello)
+catch
+ message -> "Поймана ошибка с сообщением #{message}."
+after
+ IO.puts("Я выполнюсь всегда")
+end
+#=> Я выполнюсь всегда
+# "Поймана ошибка с сообщением hello."
+
+## ---------------------------
+## -- Модули и функции
+## ---------------------------
+
+# Анонимные функции (обратите внимание на точку при вызове функции)
+square = fn(x) -> x * x end
+square.(5) #=> 25
+
+# Анонимные функции принимают клозы и гарды.
+#
+# Клозы (от англ. clause) — варианты исполнения функции.
+#
+# Гарды (от англ. guard) — охранные выражения, уточняющие сопоставление с
+# образцом в функциях. Гарды следуют после ключевого слова `when`.
+f = fn
+ x, y when x > 0 -> x + y
+ x, y -> x * y
+end
+
+f.(1, 3) #=> 4
+f.(-1, 3) #=> -3
+
+# В Elixir много встроенных функций.
+# Они доступны в текущей области видимости.
+is_number(10) #=> true
+is_list("hello") #=> false
+elem({1,2,3}, 0) #=> 1
+
+# Вы можете объединить несколько функций в модуль. Внутри модуля используйте `def`,
+# чтобы определить свои функции.
+defmodule Math do
+ def sum(a, b) do
+ a + b
+ end
+
+ def square(x) do
+ x * x
+ end
+end
+
+Math.sum(1, 2) #=> 3
+Math.square(3) #=> 9
+
+# Чтобы скомпилировать модуль Math, сохраните его в файле `math.ex`
+# и наберите в терминале: `elixirc math.ex`
+
+defmodule PrivateMath do
+ # Публичные функции начинаются с `def` и доступны из других модулей.
+ def sum(a, b) do
+ do_sum(a, b)
+ end
+
+ # Приватные функции начинаются с `defp` и доступны только внутри своего модуля.
+ defp do_sum(a, b) do
+ a + b
+ end
+end
+
+PrivateMath.sum(1, 2) #=> 3
+PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError)
+
+# Функции внутри модуля тоже принимают клозы и гарды
+defmodule Geometry do
+ def area({:rectangle, w, h}) do
+ w * h
+ end
+
+ def area({:circle, r}) when is_number(r) do
+ 3.14 * r * r
+ end
+end
+
+Geometry.area({:rectangle, 2, 3}) #=> 6
+Geometry.area({:circle, 3}) #=> 28.25999999999999801048
+Geometry.area({:circle, "not_a_number"}) #=> ** (FunctionClauseError)
+
+# Из-за неизменяемых переменных в Elixir важную роль играет рекурсия
+defmodule Recursion do
+ def sum_list([head | tail], acc) do
+ sum_list(tail, acc + head)
+ end
+
+ def sum_list([], acc) do
+ acc
+ end
+end
+
+Recursion.sum_list([1,2,3], 0) #=> 6
+
+# Модули в Elixir поддерживают атрибуты.
+# Атрибуты бывают как встроенные, так и ваши собственные.
+defmodule MyMod do
+ @moduledoc """
+ Это встроенный атрибут
+ """
+
+ @my_data 100 # А это ваш атрибут
+ IO.inspect(@my_data) #=> 100
+end
+
+# Одна из фишек языка — оператор `|>`
+# Он передаёт выражение слева в качестве первого аргумента функции справа:
+Range.new(1,10)
+|> Enum.map(fn x -> x * x end)
+|> Enum.filter(fn x -> rem(x, 2) == 0 end)
+#=> [4, 16, 36, 64, 100]
+
+## ---------------------------
+## -- Структуры и исключения
+## ---------------------------
+
+# Структуры — это расширения поверх карт, привносящие в Elixir значения по
+# умолчанию, проверки на этапе компиляции и полиморфизм.
+defmodule Person do
+ defstruct name: nil, age: 0, height: 0
+end
+
+joe_info = %Person{ name: "Joe", age: 30, height: 180 }
+#=> %Person{age: 30, height: 180, name: "Joe"}
+
+# Доступ к полю структуры
+joe_info.name #=> "Joe"
+
+# Обновление поля структуры
+older_joe_info = %{ joe_info | age: 31 }
+#=> %Person{age: 31, height: 180, name: "Joe"}
+
+# Блок `try` с ключевым словом `rescue` используется для обработки исключений
+try do
+ raise "какая-то ошибка"
+rescue
+ RuntimeError -> "перехвачена ошибка рантайма"
+ _error -> "перехват любой другой ошибки"
+end
+#=> "перехвачена ошибка рантайма"
+
+# У каждого исключения есть сообщение
+try do
+ raise "какая-то ошибка"
+rescue
+ x in [RuntimeError] ->
+ x.message
+end
+#=> "какая-то ошибка"
+
+## ---------------------------
+## -- Параллелизм
+## ---------------------------
+
+# Параллелизм в Elixir построен на модели акторов. Для написания
+# параллельной программы нам понадобятся три вещи:
+# 1. Создание процессов
+# 2. Отправка сообщений
+# 3. Приём сообщений
+
+# Новый процесс создаётся функцией `spawn`, которая принимает функцию
+# в качестве аргумента.
+f = fn -> 2 * 2 end #=> #Function<erl_eval.20.80484245>
+spawn(f) #=> #PID<0.40.0>
+
+# `spawn` возвращает идентификатор процесса (англ. process identifier, PID).
+# Вы можете использовать PID для отправки сообщений этому процессу. Сообщения
+# отправляются через оператор `send`. А для приёма сообщений используется
+# механизм `receive`:
+
+# Блок `receive do` ждёт сообщений и обработает их, как только получит. Блок
+# `receive do` обработает лишь одно полученное сообщение. Чтобы обработать
+# несколько сообщений, функция, содержащая блок `receive do`, должна рекурсивно
+# вызывать себя.
+
+defmodule Geometry do
+ def area_loop do
+ receive do
+ {:rectangle, w, h} ->
+ IO.puts("Площадь = #{w * h}")
+ area_loop()
+ {:circle, r} ->
+ IO.puts("Площадь = #{3.14 * r * r}")
+ area_loop()
+ end
+ end
+end
+
+# Скомпилируйте модуль и создайте процесс
+pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0>
+# Альтернативно
+pid = spawn(Geometry, :area_loop, [])
+
+# Отправьте сообщение процессу
+send pid, {:rectangle, 2, 3}
+#=> Площадь = 6
+# {:rectangle,2,3}
+
+send pid, {:circle, 2}
+#=> Площадь = 12.56
+# {:circle,2}
+
+# Кстати, интерактивная консоль — это тоже процесс.
+# Чтобы узнать текущий PID, воспользуйтесь встроенной функцией `self`
+self() #=> #PID<0.27.0>
+
+## ---------------------------
+## -- Агенты
+## ---------------------------
+
+# Агент — это процесс, который следит за некоторым изменяющимся значением.
+
+# Создайте агента через `Agent.start_link`, передав ему функцию.
+# Начальным состоянием агента будет значение, которое эта функция возвращает.
+{ok, my_agent} = Agent.start_link(fn -> ["красный", "зелёный"] end)
+
+# `Agent.get` принимает имя агента и анонимную функцию `fn`, которой будет
+# передано текущее состояние агента. В результате вы получите то, что вернёт
+# анонимная функция.
+Agent.get(my_agent, fn colors -> colors end) #=> ["красный", "зелёный"]
+
+# Похожим образом вы можете обновить состояние агента
+Agent.update(my_agent, fn colors -> ["синий" | colors] end)
+```
+
+## Ссылки
+
+* [Официальный сайт](http://elixir-lang.org)
+* [Шпаргалка по языку](http://media.pragprog.com/titles/elixir/ElixirCheat.pdf)
+* [Книга "Programming Elixir"](https://pragprog.com/book/elixir/programming-elixir)
+* [Книга "Learn You Some Erlang for Great Good!"](http://learnyousomeerlang.com/)
+* [Книга "Programming Erlang: Software for a Concurrent World"](https://pragprog.com/book/jaerlang2/programming-erlang)