Вы можете использовать хвостовую рекурсию для достижения этой цели:
defmodule Test do
def f(list, acc \\ [])
def f([x, y | xs], acc), do: f(xs, [{x, y} | acc])
def f(_, acc), do: Enum.into(acc, %{})
end
Это решение более эффективно по сравнению с другими предлагаемые решения. Я написал следующий модуль, чтобы иметь возможность для сравнения различных решений:
defmodule Benchmark do
# My solution
def alex(xs, acc \\ [])
def alex([x, y | xs], acc), do: alex(xs, [{x, y} | acc])
def alex(_, acc), do: Map.new(acc)
# nietaki's solution
def nietaki(xs) do
xs
|> Enum.chunk(2)
|> Enum.map(fn [x, y] -> {x, y} end)
|> Map.new()
end
# Sheharyar's solution
def sheharyar(xs) do
xs
|> Enum.chunk(2)
|> Map.new(fn [x, y] -> {x, y} end)
end
# Your solution
def chip(xs) do
Enum.reduce(xs, %{}, fn(item, acc) ->
case Map.get(acc, :last) do
nil ->
Map.put(acc, :last, item)
last ->
acc = Map.put(acc, item, last)
Map.drop(acc, [:last])
end
end)
end
# Patrick's solution
def patrick(xs) do
xs
|> Enum.chunk(2)
|> Enum.into(%{}, fn [x, y] -> {x, y} end)
end
# Function to do the time benchmarks.
def timed(f, list, times \\ 10) do
tests =
for _ <- 0..times do
:timer.tc(fn -> apply(__MODULE__, f, [list]) end) |> elem(0)
end
avg = Enum.sum(tests)/times
{f, avg}
end
# Test function.
def test(list, times \\ 10) do
list = Enum.to_list(list)
[:alex, :chip, :patrick, :nietaki, :sheharyar]
|> Enum.map(fn f -> timed(f, list, times) end)
|> Enum.sort(fn {_, x}, {_, y} -> x < y end)
end
end
Так bechmark для небольших списков является следующее:
iex(1)> Benchmark.test(0..4)
[alex: 0.2, nietaki: 0.5, sheharyar: 0.6, chip: 0.8, patrick: 4.4]
И для больших списков следующий:
iex(2)> Benchmark.test(0..1_000_000)
[alex: 143105.7, nietaki: 241233.4, sheharyar: 254751.9, patrick: 501678.9, chip: 801616.5]
Результаты - среднее время работы в микросекундах, а меньшее - лучше. Как вы можете видеть, хорошая релаксация хвоста (Benchmark.alex/1
) делает лучшую работу в этом случае.
Я надеюсь, что это поможет :)
Один важный вопрос: что вы хотите, когда в исходном списке есть нечетное количество элементов? Отбросить последнее значение или присвоить ему значение по умолчанию «nil», например? Со всеми решениями, использующими 'Enum.chunk', вы можете указать значения по умолчанию через' Enum.chunk (2, 2, [nil]) ', тогда как простой' Enum.chunk (2) 'будет отбрасывать куски, которые невозможно полностью заполнить , –