1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
---
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
#=> "какая-то ошибка"
```
|