Почему Elixir group_by использует в реализации обратную функцию? - PullRequest
1 голос
/ 16 июня 2019

Вот реализация Elixir Enum.group_by/3 от 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 Ответ

2 голосов
/ 16 июня 2019

Это сохранение порядка сгруппированных элементов.

Вот пример использования стандартного 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] prepend, и , чтобы гарантировать, что порядок элементов в группах такой же, как у исходного перечислимого, необходимо сначала обратить всплывающее перечисление.

...