Исходный код Collectable протокол
Протокол для обхода структур данных.
Функция Enum.into/2 использует этот протокол для вставки перечисляемого в коллекцию:
iex> Enum.into([a: 1, b: 2], %{})
%{a: 1, b: 2}
Почему Collectable?
Протокол Enumerable полезен для извлечения значений из коллекции. Для поддержки широкого спектра значений, функции, предоставляемые протоколом Enumerable, не сохраняют структуру. Например, передача карты в Enum.map/2 всегда возвращает список.
Этот дизайн преднамерен. Протокол Enumerable был разработан для поддержки бесконечных коллекций, ресурсов и других структур с фиксированной формой. Например, вставка значений в Range не имеет смысла, так как он имеет фиксированную структуру, где хранятся только пределы диапазона и шаг.
Модуль Collectable был разработан для заполнения пробела, оставленного протоколом Enumerable. Collectable.into/1 можно рассматривать как противоположность Enumerable.reduce/3. Если функции в Enumerable относятся к извлечению значений, то Collectable.into/1 относится к сбору этих значений в структуру.
Примеры
Чтобы показать, как вручную использовать протокол Collectable, давайте поработаем со упрощенной реализацией для MapSet.
iex> {initial_acc, collector_fun} = Collectable.into(MapSet.new())
iex> updated_acc = Enum.reduce([1, 2, 3], initial_acc, fn elem, acc ->
...> collector_fun.(acc, {:cont, elem})
...> end)
iex> collector_fun.(updated_acc, :done)
MapSet.new([1, 2, 3])
Чтобы продемонстрировать, как может быть реализован протокол, мы можем еще раз взглянуть на упрощенную реализацию для MapSet. В этой реализации "сбор" элементов просто означает вставку их в множество через MapSet.put/2.
defimpl Collectable, for: MapSet do
def into(map_set) do
collector_fun = fn
map_set_acc, {:cont, elem} ->
MapSet.put(map_set_acc, elem)
map_set_acc, :done ->
map_set_acc
_map_set_acc, :halt ->
:ok
end
initial_acc = map_set
{initial_acc, collector_fun}
end
end
Теперь мы можем вызвать Enum.into/2:
iex> Enum.into([1, 2, 3], MapSet.new()) MapSet.new([1, 2, 3])
Краткое описание
Типы
- t()
Все типы, которые реализуют этот протокол.
Функции
- into(collectable)
Возвращает начальное значение аккумулятора и функцию "сборщика".
Типы
command()Исходный код
@type command() :: {:cont, term()} | :done | :halt t()Исходный код
@type t() :: term()
Все типы, которые реализуют этот протокол.
Функции
into(collectable)Исходный код
@spec into(t()) ::
{initial_acc :: term(), collector :: (term(), command() -> t() | term())} Возвращает начальное значение аккумулятора и функцию "сборщика".
Принимает collectable, который может быть использован в качестве начального значения аккумулятора, которое будет передано в функцию.
Функция сборщика получает терм и команду и вставляет терм в аккумулирующий collectable на каждой команде {:cont, term}.
:done передаётся как команда, когда больше значений не будет вставлено. Это полезно, когда нужно закрыть ресурсы или нормализовать значения. Collectable должен быть возвращён, когда команда - :done.
Если вставка внезапно прервана, :halt передаётся, и функция может вернуть любое значение, так как оно не будет использовано.
Примеры использования протокола Collectable и into/1 см. в документации модуля.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/Collectable.html