檢視原始碼 可收集的 通訊協定 (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])
摘要
類型
函數
傳回一個初始累加器和一個「收集器」函數。
接收一個 可收集
,可用作傳遞給函數的初始累加器。
收集器函數接收一個術語和一個命令,並在每個 {:cont, term}
命令中將術語注入可收集累加器。
當不再注入進一步的值時,:done
會作為命令傳遞。當需要關閉資源或標準化值時,這很有用。當命令為 :done
時,必須傳回一個可收集。
如果注入突然中斷,會傳遞 :halt
,函數可以傳回任何值,因為它不會被使用。