檢視原始碼 可收集的 通訊協定 (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])

摘要

類型

t()

所有實作此通訊協定的類型。

函數

傳回一個初始累加器和一個「收集器」函數。

類型

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

所有實作此通訊協定的類型。

函數

@spec into(t()) ::
  {initial_acc :: term(), collector :: (term(), command() -> t() | term())}

傳回一個初始累加器和一個「收集器」函數。

接收一個 可收集,可用作傳遞給函數的初始累加器。

收集器函數接收一個術語和一個命令,並在每個 {:cont, term} 命令中將術語注入可收集累加器。

當不再注入進一步的值時,:done 會作為命令傳遞。當需要關閉資源或標準化值時,這很有用。當命令為 :done 時,必須傳回一個可收集。

如果注入突然中斷,會傳遞 :halt,函數可以傳回任何值,因為它不會被使用。

有關如何使用 可收集 協定和 into/1 的範例,請參閱模組文件。