Spec-Zone.ru › Elixir 1.5

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

Протокол перечислимого типа, используемый модулями 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 определена с помощью хвостовой рекурсии. Если функция хвостово-рекурсивна, весь состояние передаётся как аргументы, поэтому продолжение будет просто частично применённой функцией сокращения.

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.5.3/Enumerable.html

Spec-Zone.ru

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