Spec-Zone.ru › Elixir 1.4

Перечислимый протокол

Протокол перечислимого, используемый модулями Enum и Stream.

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

Enum.map([1, 2, 3], &(&1 * 2))

вызывает Enumerable.reduce/3 для выполнения операции сворачивания, которая строит сопоставленный список, вызывая функцию сопоставления &(&1 * 2) для каждого элемента в коллекции и потребляя элемент с накопленным списком.

Внутри Enum.map/2 реализуется следующим образом:

def map(enum, fun) do
  reducer = fn x, acc -> {:cont, [fun.(x) | acc]} end
  Enumerable.reduce(enum, {:cont, []}, reducer) |> elem(1) |> :lists.reverse()
end

Обратите внимание, что функция, предоставленная пользователем, обернута в функцию reducer/0. Функция reducer/0 должна возвращать помеченный кортеж после каждой итерации, как описано в типе acc/0.

Причина, по которой накопитель требует помеченного кортежа, заключается в том, чтобы позволить функции reducer/0 сообщить об окончании перечисления основному перечисляемому объекту, позволяя корректно закрыть любые открытые ресурсы. Это также позволяет приостановить перечисление, что полезно при необходимости чередования между многими перечисляемыми объектами (как в zip).

Наконец, Enumerable.reduce/3 вернёт другой помеченный кортеж, представленный типом result/0.

Краткое описание

Типы

acc()

Значение накопителя на каждом шаге

continuation()

Частично применённая функция reduce

reducer()

Функция reducer

result()

Результат операции reduce

t()

Функции

count(enumerable)

Возвращает размер перечислимого объекта

member?(enumerable, element)

Проверяет, существует ли элемент в перечислимом объекте

reduce(enumerable, acc, fun)

Сворачивает перечислимый объект в элемент

Типы

acc()

acc() :: {:cont, term()} | {:halt, term()} | {:suspend, term()}

Значение накопителя на каждом шаге.

Это должен быть помеченный кортеж с одним из следующих «меток»:

  • :cont - перечисление должно продолжить работу
  • :halt - перечисление должно немедленно остановиться
  • :suspend - перечисление должно быть немедленно приостановлено

В зависимости от значения накопителя, результат, возвращаемый Enumerable.reduce/3, будет меняться. Для получения дополнительной информации см. документацию по типу result/0.

В случае, если функция reducer/0 возвращает накопитель :suspend, он должен быть явно обработан вызывающей стороной и никогда не должен утечь.

continuation()

continuation() :: (acc() -> result())

Частично применённая функция reduce.

Продолжение — это замыкание, возвращаемое в результате приостановки перечисления. При вызове оно ожидает новый накопитель и возвращает результат.

Продолжение легко реализовать, если функция reduce определена в хвостовой рекурсии. Если функция хвостово-рекурсивна, все состояния передаются в качестве аргументов, поэтому продолжение просто будет частично применённой функцией reduce.

reducer()

reducer() :: (term(), term() -> acc())

Функция reducer.

Должна вызываться с элементом перечислимого объекта и содержимым накопителя.

Возвращает накопитель для следующего шага перечисления.

result()

result() ::
  {:done, term()} |
  {:halted, term()} |
  {:suspended, term(), continuation()}

Результат операции reduce.

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

В случае, если функция reducer/0 возвращает накопитель :suspend, кортеж :suspended должен быть явно обработан вызывающей стороной и никогда не должен утечь. На практике это означает, что обычные функции перечисления должны только заботиться о результатах :done и :halted.

Кроме того, вызов :suspend всегда должен сопровождаться другим вызовом, в конечном итоге останавливая или продолжая перечисление до конца.

t()

t() :: term()

Функции

count(enumerable)

count(t()) :: {:ok, non_neg_integer()} | {:error, module()}

Возвращает размер перечислимого объекта.

Должен возвращать {:ok, size}.

Если возвращается {:error, __MODULE__}, используется алгоритм по умолчанию с использованием reduce и оператора соответствия (===). Этот алгоритм работает за линейное время.

Пожалуйста, используйте алгоритм по умолчанию, если не можете реализовать алгоритм, значительно более быстрый.

member?(enumerable, element)

member?(t(), term()) :: {:ok, boolean()} | {:error, module()}

Проверяет, существует ли элемент в перечислимом объекте.

Должен возвращать {:ok, boolean}.

Если возвращается {:error, __MODULE__}, используется алгоритм по умолчанию с использованием reduce и оператора соответствия (===). Этот алгоритм работает за линейное время.

Пожалуйста, используйте алгоритм по умолчанию, если не можете реализовать алгоритм, значительно более быстрый.

reduce(enumerable, acc, fun)

reduce(t(), acc(), reducer()) :: result()

Сворачивает перечислимый объект в элемент.

Большинство операций в Enum реализованы с помощью reduce. Эта функция должна применять заданную функцию reducer/0 к каждому элементу в перечислимом объекте и продолжить ожидаемым образом, как указано возвращаемым накопителем.

В качестве примера, здесь представлена реализация reduce для списков:

def reduce(_,       {:halt, acc}, _fun),   do: {:halted, acc}
def reduce(list,    {:suspend, acc}, fun), do: {:suspended, acc, &reduce(list, &1, fun)}
def reduce([],      {:cont, acc}, _fun),   do: {:done, acc}
def reduce([h | t], {:cont, acc}, fun),    do: reduce(t, fun.(h, acc), fun)

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.4.5/Enumerable.html

Spec-Zone.ru

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