Collectable

遍历数据结构的协议。

Enum.into/2函数使用此协议将枚举插入到集合中:

iex> Enum.into([a: 1, b: 2], %{})
%{a: 1, b: 2}

为什么要收藏?

Enumerable协议有助于从集合中取出值。为了支持各种各样的值,Enumerable协议提供的功能不会保持形状。例如,传递一个地图Enum.map/2总是返回一个列表。

这个设计是有意的。Enumerable用于支持无限的集合、资源和其他固定形状的结构。例如,将值插入到范围中是没有意义的,因为它有一个固定的形状,仅存储范围限制。

Collectable模块旨在填补Enumerable协议留下的空白。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<[1, 2, 3]>

为了展示协议如何实现,我们可以再次看看实现MapSet。在这个实现中,“收集”元素仅仅意味着将它们插入到集合中MapSet.put/2

defimpl Collectable do
  def into(original) do
    collector_fun = fn
      set, {:cont, elem} -> MapSet.put(set, elem)
      set, :done -> set
      _set, :halt -> :ok
    end

    {original, collector_fun}
  end
end

类型

command()t()

功能

into(collectable)

返回初始累加器和“收集器”函数。

command()

command() :: {:cont, term} | :done | :halt

t()

t() :: term

into(collectable)

into(t) :: {term, (term, command -> t | term)}

返回一个初始累加器和一个“收集器”函数。

返回的函数接收一个术语和一个命令,并将该项注入到每个{:cont, term}命令。

:done当没有更多值将被注入时作为命令传递。这在需要关闭资源或标准化值时非常有用。必须在收到命令时返回收集器:done

如果注射突然中断,:halt函数可以返回任何值,因为它不会被使用。

有关如何使用Collectable议定书和into/1请参阅模块文档。

扫码关注云+社区

领取腾讯云代金券