Вирази
Спискові вирази - це синтаксичний цукор для проходження по перечисленням в Elixir. На цьому уроці ми поглянемо на те, як можна використати спискові вирази для ітерації та генерації.
Основи
Часто спискові вирази використовуються для створення стисліших визначень для ітерації Enum
і Stream
.
Давайте поглянемо на простий вираз і розберемо його детальніше:
iex> list = [1, 2, 3, 4, 5]
iex> for x <- list, do: x*x
[1, 4, 9, 16, 25]
Перше, що ми помічаємо - це використання for
і генератора.
Що таке генератор?
Генератори - це вирази подібні до x <- [1, 2, 3, 4]
, які можна знайти в спискових виразах.
Вони відповідальні за генерування наступного значення.
На щастя, використання виразів не обмежується списками; вони можуть працювати з будь-якими перечисленнями:
# Ключові списки
iex> for {_key, val} <- [one: 1, two: 2, three: 3], do: val
[1, 2, 3]
# Асоціативні масиви
iex> for {k, v} <- %{"a" => "A", "b" => "B"}, do: {k, v}
[{"a", "A"}, {"b", "B"}]
# Бінарні значення
iex> for <<c <- "hello">>, do: <<c>>
["h", "e", "l", "l", "o"]
Як і багато інших речей в Elixir, генератори покладаються на зіставлення зі зразком для того, щоб порівнювати вхідний набір зі змінною зліва. У випадку, якщо співпадіння не знайдено, значення ігнорується:
iex> for {:ok, val} <- [ok: "Hello", error: "Unknown", ok: "World"], do: val
["Hello", "World"]
Також можна використовувати кілька генераторів, подібно до вкладених циклів:
iex> list = [1, 2, 3, 4]
iex> for n <- list, times <- 1..n do
...> String.duplicate("*", times)
...> end
["*", "*", "**", "*", "**", "***", "*", "**", "***", "****"]
Щоб краще проілюструвати зациклення, що стається, давайте використаємо IO.puts
і відобразимо два згенерованих значення:
iex> for n <- list, times <- 1..n, do: IO.puts "#{n} - #{times}"
1 - 1
2 - 1
2 - 2
3 - 1
3 - 2
3 - 3
4 - 1
4 - 2
4 - 3
4 - 4
Спискові вирази - це синтаксичний цукор і повинні використовуватися лише там, де це доречно.
Фільтри
Фільтри можна сприймати як певну варту для виразів.
Коли відфільтроване значення повертає false
або nil
- воно виключається з фінального списку.
Давайте проітеруємо по ряду і припустимо, що нам потрібні лише парні числа.
Ми використаємо функцію is_even/1
з модулю Integer
, щоб визначити, яким є значення: парним чи непарним.
import Integer
iex> for x <- 1..10, is_even(x), do: x
[2, 4, 6, 8, 10]
Подібно до генераторів, ми можемо використовувати декілька фільтрів одночасно. Давайте розширимо наш ряд і потім відфільтруємо, щоб залишити значення, що одночасно є парними і націло діляться на 3.
import Integer
iex> for x <- 1..100,
...> is_even(x),
...> rem(x, 3) == 0, do: x
[6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]
Використання :into
А що якби ми хотіли створити щось окрім списку?
З опцією :into
ми це можемо зробити!
Загалом, :into
приймає будь-яку структуру, що імплементує протокол Collectable
.
Давайте створимо асоціативний масив з ключового списку використовуючи :into
:
iex> for {k, v} <- [one: 1, two: 2, three: 3], into: %{}, do: {k, v}
%{one: 1, three: 3, two: 2}
Оскільки бінарні значення належать до Collectable
, ми можемо використати спискові вирази і :into
щоб створити стрічки:
iex> for c <- [72, 101, 108, 108, 111], into: "", do: <<c>>
"Hello"
Ось і все! Спискові вирази - це простий і стислий спосіб ітерувати колекції.
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!