Spec-Zone.ru › Elixir 1.17

Исходный код Генераторы

В Elixir часто требуется итерироваться по Enumerable, часто отфильтровывая некоторые результаты и отображая значения в другой список. Генераторы — это синтаксический сахар для таких конструкций: они объединяют эти распространённые задачи в специальную форму for.

Например, мы можем отобразить список целых чисел в их квадраты:

iex> for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]

Генератор состоит из трёх частей: генераторов, фильтров и собираемых элементов.

Генераторы и фильтры

В выражении выше, n <- [1, 2, 3, 4] является генератором. Он буквально генерирует значения, которые будут использоваться в генераторе. Любой перечисляемый объект можно передать в правой части выражения генератора:

iex> for n <- 1..4, do: n * n
[1, 4, 9, 16]

Выражения генераторов также поддерживают сопоставление с образцом в левой части; все несоответствующие образцы игнорируются. Представьте, что вместо диапазона у нас есть список ключевых слов, где ключ — атом :good или :bad, и мы хотим вычислить квадрат только значений :good:

iex> values = [good: 1, good: 2, bad: 3, good: 4]
iex> for {:good, n} <- values, do: n * n
[1, 4, 16]

В качестве альтернативы сопоставлению с образцом можно использовать фильтры для выбора определённых элементов. Например, мы можем выбрать кратные 3 и отбросить все остальные:

iex> for n <- 0..5, rem(n, 3) == 0, do: n * n
[0, 9]

Генераторы отбрасывают все элементы, для которых выражение фильтра возвращает false или nil; все остальные значения выбираются.

Генераторы обычно обеспечивают более лаконичное представление, чем использование эквивалентных функций из модулей Enum и Stream. Кроме того, генераторы также позволяют задавать несколько генераторов и фильтров. Вот пример, который получает список каталогов и получает размер каждого файла в этих каталогах:

dirs = ["/home/mikey", "/home/james"]

for dir <- dirs,
    file <- File.ls!(dir),
    path = Path.join(dir, file),
    File.regular?(path) do
  File.stat!(path).size
end

Также можно использовать несколько генераторов для вычисления декартова произведения двух списков:

iex> for i <- [:a, :b, :c], j <- [1, 2], do:  {i, j}
[a: 1, a: 2, b: 1, b: 2, c: 1, c: 2]

Наконец, имейте в виду, что присваивание переменных внутри генератора, будь то в генераторах, фильтрах или внутри блока, не отражается за пределами генератора.

Генераторы битовых строк

Также поддерживаются генераторы битовых строк, которые очень полезны, когда вам нужно использовать потоки битовых строк. В приведенном ниже примере поступает список пикселей из двоичного файла с соответствующими красными, зелёными и синими значениями и преобразует их в кортежи по три элемента:

iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex> for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
[{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]

Генератор битовой строки можно смешивать с обычными генераторами перечисляемых объектов и поддерживает фильтры.

Опция :into

В приведённых выше примерах все генераторы возвращали списки в качестве результата. Однако результат генератора можно вставить в различные структуры данных, передав опцию :into генератору.

Например, генератор битовой строки можно использовать с опцией :into для лёгкого удаления всех пробелов в строке:

iex> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"

Множества, карты и другие словари также можно передать в опцию :into. В общем, :into принимает любую структуру, которая реализует протокол Collectable.

Распространённым случаем использования :into является преобразование значений в карте:

iex> for {key, val} <- %{"a" => 1, "b" => 2}, into: %{}, do: {key, val * val}
%{"a" => 1, "b" => 4}

Давайте рассмотрим ещё один пример с потоками. Поскольку модуль IO предоставляет потоки (которые являются как Enumerable, так и Collectable), терминал эха, который возвращает строку в верхнем регистре, можно реализовать с помощью генераторов:

iex> stream = IO.stream(:stdio, :line)
iex> for line <- stream, into: stream do
...>   String.upcase(line) <> "\n"
...> end

Теперь введите любую строку в терминал, и вы увидите, что то же самое значение будет выведено в верхнем регистре. К сожалению, этот пример также заблокировал ваш IEx интерпретатор в генераторе, поэтому вам нужно нажать Ctrl+C дважды, чтобы выйти из него. :)

Другие опции

Генераторы поддерживают другие опции, такие как :reduce и :uniq. Вот дополнительные ресурсы для получения дополнительной информации о генераторах:

  • for официальная ссылка в документации Elixir
  • Полноценное руководство Митчелла Ханберга по генераторам Elixir
← Предыдущая страница Протоколы
Следующая страница → Знаки

Загрузить версию ePub

Создано с помощью ExDoc (v0.34.1) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/comprehensions.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API