Enum
Набір алгоритмів для операцій із перерахуваннями.
Enum
Модуль Enum
включає в себе більш ніж 70 функцій для роботи з перерахуваннями.
Всі колекції, про які ми дізналися в попередньому уроці, за виключенням кортежів, є перерахуваннями.
Цей урок містить лише невелику частину доступних функцій, проте ми можемо вивчити їх самостійно. Давайте проведемо невеличкий експеримент в IEx.
iex> Enum.__info__(:functions) |> Enum.each(fn({function, arity}) ->
...> IO.puts "#{function}/#{arity}"
...> end)
all?/1
all?/2
any?/1
any?/2
at/2
at/3
...
Стає зрозуміло, що у нас є велика кількість функцій. Перерахування лежить в основі функціонального програмування, і в комбінації з іншими особливостями Elixir може надзвичайно розширити можливості розробника.
Для того, щоб побачити повний список функцій - відвідайте офіційну документацію по Enum
. Для лінивих перечислень скористайтеся модулем Stream
.
all?
При використанні all?/2
, як і більшої частини Enum
в цілому, ми передаємо функцію, яка буде застосовуватися до елементів колекції.
У випадку з all?/2
, якщо хоча б один елемент колекції не поверне true
у якості результату, то результатом буде false
:
iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 3 end)
false
iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 1 end)
true
any?
І навпаки, any?/2
поверне true
, якщо хоча б один елемент колекції буде true
:
iex> Enum.any?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 5 end)
true
chunk_every
Якщо потрібно розбити колекцію на менші групи, метод chunk_every/2
— саме те, що потрібно:
iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]
Також є декілька додаткових опцій, які ви можете подивитися в офіціальній документації: chunk_every/4
.
chunk_by
Якщо потрібно розділити колекцію за якоюсь іншою ознакою, окрім кількості, можна використати функцію chunk_by/2
.
Вона приймає перерахування і функцію. Коли значення, що повертає функція, змінюється, починається формування нової групи:
iex> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"]]
iex> Enum.chunk_by(["one", "two", "three", "four", "five", "six"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six"]]
map_every
Іноді недостатньо просто розбити колекцію на частини.
Якщо колекція впорядкована належним чином, можна використати функцію map_every/3
, яка дозволяє пройтись тільки по тим елементам, які нас цікавлять, починаючи з першого:
# Застосовуємо функцію до кожного 3-го елемента
iex> Enum.map_every([1, 2, 3, 4, 5, 6, 7, 8], 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8]
each
Коли потрібно обійти колекцію без створення нового значення, використовується метод each/2
:
iex> Enum.each(["one", "two", "three"], fn(s) -> IO.puts(s) end)
one
two
three
:ok
Зауваження: Метод each/2
повертає атом :ok
.
map
Коли потрібно застосувати функцію до кожного елементу та створити нову колекцію, можна використати функцію map/2
:
iex> Enum.map([0, 1, 2, 3], fn(x) -> x - 1 end)
[-1, 0, 1, 2]
min
Функція min/1
повертає мінімальне значення колекції:
iex> Enum.min([5, 3, 0, -1])
-1
min/2
робить те ж саме, але дозволяє вказати значення за замовчування в анонімній функції:
iex> Enum.min([], fn -> :foo end)
:foo
max
Функція max/1
повертає максимальне значення колекції:
iex> Enum.max([5, 3, 0, -1])
5
max/2
робить те ж саме, але, як і в прикладі з min/2
, дозволяє передати значення за замовчування:
iex> Enum.max([], fn -> :bar end)
:bar
filter
Функція filter/2
дозволяє нам фільтрувати колекцію таким чином, щоби включати лише ті елементи, котрі при виконанні переданої функції повертають true
у якості результату.
iex> Enum.filter([1, 2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
[2, 4]
reduce
Функція reduce/3
дає можливість об’єднати усі елементи колекції в одне значення.
Для цього ви можете передати в функцію опціональне значення-акумулятор (10
в даному випадку).
Якщо акумулятор не переданий, використовується перший елемент в перечисленні:
iex> Enum.reduce([1, 2, 3], 10, fn(x, acc) -> x + acc end)
16
iex> Enum.reduce([1, 2, 3], fn(x, acc) -> x + acc end)
6
iex> Enum.reduce(["a","b","c"], "1", fn(x,acc)-> x <> acc end)
"cba1"
sort
Для сортування колекцій доступна не одна, а цілих дві функції sort
.
sort/1
використовує вбудований в Erlang механізм порівняння:
iex> Enum.sort([5, 6, 1, 3, -1, 4])
[-1, 1, 3, 4, 5, 6]
iex> Enum.sort([:foo, "bar", Enum, -1, 4])
[-1, 4, Enum, :foo, "bar"]
sort/2
дозволяє передати власну функцію для порівняння елементів:
# з нашою функцією
iex> Enum.sort([%{:val => 4}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)
[%{val: 4}, %{val: 1}]
# без неї
iex> Enum.sort([%{:count => 4}, %{:count => 1}])
[%{count: 1}, %{count: 4}]
uniq
Ми можемо використати uniq/1
для того, щоб видалити дублікати з наших перерахувань.
iex> Enum.uniq([1, 2, 3, 2, 1, 1, 1, 1, 1])
[1, 2, 3]
uniq_by
uniq_by/2
також видаляє дублікати з перерахувань, але дозволяє нам вказати функцію для того, щоб здійснити унікальне порівняння.
iex> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 3}], fn coord -> coord.y end)
[%{x: 1, y: 1}, %{x: 3, y: 3}]
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!