ライブラリの実装を眺めてみましたが両者に決定的な違いはなさそうに思えました。
実行される命令の数でいうとKernel.map_size/1
の方が少ないので、実行時間は短くなるはずです。しかしEnum.count/1
の実行時間との違いはごくわずかだと思いますので、その違いに気づくことはなさそうです。
Kernel.map_size/1
の実装はErlangのBIF(Built-In Function、組み込み関数)である :erlang.map_size/1
を呼ぶようになっています。
https://github.com/elixir-lang/elixir/blob/v1.9.1/lib/elixir/lib/kernel.ex#L740-L759
elixir
1 def map_size(map) do
2 :erlang.map_size(map)
3 end
erlang.map_size/1
はCで書かれており、Mapの内部構造で管理している要素数を返しています。そのため、Mapの要素数に関係なく一定時間(O(1)
)で実行できる効率の良いものになっています。
https://github.com/erlang/otp/blob/0699796062861bec095fda747afdcf01bc2f015e/erts/emulator/beam/erl_map.c#L123-L146
次にEnum.count/1
の実装です。
https://github.com/elixir-lang/elixir/blob/v1.9.1/lib/elixir/lib/enum.ex#L599-L607
elixir
1 def count(enumerable) do
2 case Enumerable.count(enumerable) do
3 {:ok, value} when is_integer(value) ->
4 value
5
6 {:error, module} ->
7 enumerable |> module.reduce({:cont, 0}, fn _, acc -> {:cont, acc + 1} end) |> elem(1)
8 end
9 end
これにMapを与えるとMapのEnumerable
実装のcount/1
が呼ばれることになります。
Mapでは以下のようにKernel.map_size/1
を呼んで、タプルを作って返しています。
https://github.com/elixir-lang/elixir/blob/v1.9.1/lib/elixir/lib/enum.ex#L3334-L3337
elixir
1defimpl Enumerable, for: Map do
2 def count(map) do
3 {:ok, map_size(map)}
4 end
このように、Enum.count/1
でも内部的にはKernel.map_size/1
が使われることは変わりません。ただ、タプルを作ったりis_integer/1
のガード付きのパターンマッチを行ったりと、わずかですが余分な処理を行います。
最初に書いたとおり実行時間の違いはごくわずかでしょうからどちらを使ってもよさそうです。ただ、どちらか1つだけを選ぶなら、効率の面からKernel.map_size/1
を選ぶことになるのかなと思います。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/09/13 11:43
2019/09/13 11:53
2019/09/13 12:00