为什么Elixir的group_by在实现中使用反向函数?

时间:2019-06-16 05:21:45

标签: elixir

这是Enum.group_by/3中Elixir的Github实现:

def group_by(enumerable, key_fun, value_fun \\ fn x -> x end)

def group_by(enumerable, key_fun, value_fun) when is_function(key_fun) do
  reduce(reverse(enumerable), %{}, fn entry, acc ->
    key = key_fun.(entry)
    value = value_fun.(entry)

    case acc do
      %{^key => existing} -> Map.put(acc, key, [value | existing])
      %{} -> Map.put(acc, key, [value])
    end
  end)
end

为什么将reverse/1函数应用于enumerable

1 个答案:

答案 0 :(得分:2)

这是为了保留已分组项目的顺序。

以下是使用标准group_by的示例:

Enum.group_by(["aa", "ab", "ac", "ba", "bb", "bc"], &String.first/1)
# %{"a" => ["aa", "ab", "ac"], "b" => ["ba", "bb", "bc"]}

如果我们在自定义实现中删除了reverse命令:

Example.no_reverse_group_by(["aa", "ab", "ac", "ba", "bb", "bc"], &String.first/1)
# %{"a" => ["ac", "ab", "aa"], "b" => ["bc", "bb", "ba"]}

您会看到分组元素"ac", "ab", "aa"的内部顺序与原始顺序"aa", "ab", "ac"的相反。

原因是因为Map.put(acc, key, [value | existing])通过将遇到的每个value元素添加到existing元素列表的开头来建立组。与原始可枚举相比,这将以相反的顺序建立项目组。

添加到列表的速度很快,但是添加到列表的末尾则需要遍历整个列表。因此,为了使算法能够使用有效的[value | existing]前置操作来确保组中项目的顺序与原始可枚举的顺序相同,必须首先反转可枚举。