Spec-Zone.ru › Elixir 1.3

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

Протокол перечислимого типа, используемый модулями 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()

Функция reduce

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)

Функция reduce.

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

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

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

Spec-Zone.ru

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