您可以使用尾递归来实现:
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
该解决方案比其他er解决方案提出。我写了下面的模块能够标杆不同的解决方案:
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]
结果是以微秒为单位的平均运行时间,并且越少越好。正如你所看到的,在这种情况下,好的ol'尾递归(Benchmark.alex/1
)做得更好。
我希望这个帮助:)
一个重要的问题是:当原始列表中有奇数个元素时,你想要发生什么?例如,删除最后一个值或为其分配一个默认值“nil”?在所有使用'Enum.chunk'的解决方案中,您可以通过'Enum.chunk(2,2,[nil])'提供默认值,而普通的'Enum.chunk(2)'将放弃无法完全填充的块。 –