查看源代码 可收集 协议 (Elixir v1.16.2)
用于遍历数据结构的协议。
该 Enum.into/2
函数使用此协议将可枚举对象插入到集合中。
iex> Enum.into([a: 1, b: 2], %{})
%{a: 1, b: 2}
为什么要可收集?
该 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])
总结
函数
返回一个初始累加器和一个“收集器”函数。
类型
函数
返回一个初始累加器和一个“收集器”函数。
接收一个 collectable
,它可以用作将传递给该函数的初始累加器。
收集器函数接收一个项和一个命令,并在每个 {:cont, term}
命令中将该项注入到可收集的累加器中。
当不再注入值时,:done
将作为命令传递。这在需要关闭资源或规范化值时很有用。当命令为 :done
时,必须返回可收集对象。
如果注入突然中断,则传递 :halt
,并且该函数可以返回任何值,因为它不会被使用。
有关如何使用 Collectable
协议和 into/1
的示例,请参阅模块文档。