functional-programming - Elixir Enum.map 与理解

标签 functional-programming elixir

我有一张 map ,我正在修改上面的每个元素,我很困惑哪种方法更好(更快)用 Enum.map 然后 Enum.into(% {}) 或用于理解,如

for {key, value} <- my_map, into: %{} do
  {key, new_value}
end

最佳答案

原始答案

您可以使用 Benchee进行这种比较。

一个简单的 Benchee 测试将显示 Enum 对于这种情况更快。

iex(1)> m = %{a: 1, b: 2, c: 3, d: 4}
%{a: 1, b: 2, c: 3, d: 4}
iex(2)> with_enum = fn -> Enum.map(m, fn {k, v} -> {k, v * v} end) end
#Function<20.127694169/0 in :erl_eval.expr/5>
iex(3)> with_for = fn -> for {k, v} <- m, into: %{}, do: {k, v * v} end
#Function<20.127694169/0 in :erl_eval.expr/5>
iex(4)> Benchee.run(%{
...(4)>   "with_enum" => fn -> with_enum.() end,
...(4)>   "with_for" => fn -> with_for.() end
...(4)> })
Operating System: Linux
CPU Information: Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz
Number of Available Cores: 4
Available memory: 7.71 GB
Elixir 1.7.4
Erlang 21.0

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 14 s


Benchmarking with_enum...
Benchmarking with_for...

Name                ips        average  deviation         median         99th %
with_enum       28.27 K       35.37 μs    ±16.16%       34.37 μs       55.21 μs
with_for        19.55 K       51.14 μs     ±9.16%       50.08 μs       59.94 μs

Comparison: 
with_enum       28.27 K
with_for        19.55 K - 1.45x slower

一般来说,for 不是 Elixir 中这些情况的最佳选择,它最适合列表理解,它可以非常快速地完成并具有易于阅读的语法。

Enum 的函数经过优化以处理这些更具迭代性的场景,就像您在其他编程语言中使用 for 构造所做的那样。


编辑

尽管我最初回答的主要目的是指向一个有助于运行此类比较的框架,以便 OP 可以自己尝试,正如另一位用户指出的那样,示例函数使用 Enum。 map 产生的结果与使用 for 的结果不同。正如他自己指出的那样,将 Enum.into 添加到 Enum.map 调用会导致有时运行时间更长的函数。所以这里有一个更新,添加了一些更多的选项,这些选项也被认为可以通过基准产生相同的结果。

iex> m = %{a: 1, b: 2, c: 3, d: 4}
%{a: 1, b: 2, c: 3, d: 4}
iex> with_enum_map_into = fn -> m |> Enum.map(fn {k, v} -> {k, v * v} end) |> Enum.into(%{}) end
#Function<...>
iex> with_enum_map_map_new = fn -> m |> Enum.map(fn {k, v} -> {k, v * v} end) |> Map.new() end
#Function<...>
iex> with_map_new = fn -> Map.new(m, fn {k, v} -> {k, v * v} end) end
#Function<...>
iex> with_reduce_map_put = fn -> Enum.reduce(m, %{}, fn {k, v}, acc -> Map.put(acc, k, v * v) end) end
#Function<...>
iex> with_reduce_map_merge = fn -> Enum.reduce(m, %{}, fn {k, v}, acc -> Map.merge(acc, %{k => v * v}) end) end
#Function<...>
iex> with_for = fn -> for {k, v} <- m, into: %{}, do: {k, v * v} end
#Function<20.127694169/0 in :erl_eval.expr/5>
iex> Benchee.run(%{                                               
...>   "with_for" => fn -> with_for.() end,                       
...>   "with_enum_map_into" => fn -> with_enum_map_into.() end,   
...>   "with_enum_map_map_new" => fn -> with_enum_map_map_new.() end,
...>   "with_map_new" => fn -> with_map_new.() end,                  
...>   "with_reduce_map_put" => fn -> with_reduce_map_put.() end,    
...>   "with_reduce_map_merge" => fn -> with_reduce_map_merge.() end 
...> })
Benchmarking with_enum_map_into...
Benchmarking with_enum_map_map_new...
Benchmarking with_for...
Benchmarking with_map_new...
Benchmarking with_reduce_map_merge...
Benchmarking with_reduce_map_put...

Name                            ips        average  deviation         median         99th %
with_enum_map_map_new       96.55 K       10.36 μs   ±158.95%        9.08 μs       37.43 μs
with_map_new                89.98 K       11.11 μs   ±154.88%        8.94 μs       41.93 μs
with_enum_map_into          87.50 K       11.43 μs   ±168.60%        9.46 μs       30.92 μs
with_reduce_map_put         84.31 K       11.86 μs    ±63.69%       10.38 μs       38.56 μs
with_reduce_map_merge       84.29 K       11.86 μs    ±91.14%       10.25 μs       38.49 μs
with_for                    61.08 K       16.37 μs    ±95.14%       14.18 μs       36.76 μs

Comparison: 
with_enum_map_map_new       96.55 K
with_map_new                89.98 K - 1.07x slower +0.76 μs
with_enum_map_into          87.50 K - 1.10x slower +1.07 μs
with_reduce_map_put         84.31 K - 1.15x slower +1.50 μs
with_reduce_map_merge       84.29 K - 1.15x slower +1.51 μs
with_for                    61.08 K - 1.58x slower +6.01 μs

在我的机器上运行基准测试时,这个顺序是一致的(因此你自己运行东西很重要),for 每次都排在最后并管道 Enum.map 进入 Map.new 一直是最快的,其次是仅使用带有映射函数的 Map.new。我确实坚持我原来的观点,即 Elixir 中的 for 主要用于理解,但它在语法上确实非常好。所有这些都是不错的选择,真的,它只是表明有几种方法可以实现同一件事,Elixir 通常就是这种情况,所以,有时它归结为偏好以及优化是否对你的目标至关重要正在做。

关于functional-programming - Elixir Enum.map 与理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55038704/

相关文章:

arrays - 从给定点列出二维数组对角线元素的最快方法

elixir - 编译错误phoenix_ecto

Elixir Slime - 在 Javascript 标签中嵌入 Elixir

elixir - 如何使用 Elixir 宏创建动态函数名称?

elixir - 您如何为 ueberauth 身份进行单独的注册回调

java - 泛型:上限通配符转换/转换错误

java - 如何迭代谓词列表

haskell - 二维 zipper

haskell - Haskell 中是否有索引列表,它是好是坏?

elixir - (RuntimeError) 不支持使用 cast/4 强制转换 assocs,请改用 cast_assoc/3