はじめに
- Elixirでいろいろな長さをはかってみたいとおもいます
- きっかけはMapの長さってどうやって調べるのだっけ? とおもったことがきっかけです
- 長さが欲しくなることは実はあんまりなかったりする気もしていますが、いろいろ試してみたこと書いておきます
この記事でのElixirのバージョン
% iex
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]
Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)
まとめ
- Enum.count/1 便利!
- 以下具体例です
List
iex> [1, 2, 3] |> length
3
iex> [1, [2, 3], 4, 5] |> Enum.count
4
Keyword
iex> [exit_on_close: true, active: :once, packet_size: 1024] |> length
3
iex> [exit_on_close: true, active: :once, packet_size: 1024] |> Enum.count
3
Map
iex> %{hoge: 1, fuga: 2} |> map_size
2
iex> %{hoge: 1, fuga: 2, foo: %{hoge: 1, fuga: 2}} |> Enum.count
3
iex> %{hoge: 1, fuga: 2, foo: %{hoge: 1, fuga: 2}} |> length
** (ArgumentError) argument error
:erlang.length(%{foo: %{fuga: 2, hoge: 1}, fuga: 2, hoge: 1})
Range
iex> 1..3 |> Enum.count
3
iex> 1..3 |> length
** (ArgumentError) argument error
:erlang.length(1..3)
文字列
iex> "あいう" |> String.length
3
iex> "あいう" |> byte_size
9
iex> "あいう" |> length
** (ArgumentError) argument error
:erlang.length("あいう")
iex> "あいう" |> Enum.count
** (Protocol.UndefinedError) protocol Enumerable not implemented for "あいう" of type BitString
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:153: Enumerable.count/1
(elixir) lib/enum.ex:600: Enum.count/1
文字リスト
-
@im_miolabさんのElixirで整数リストが「謎の文字」として返ってくる現象と、文字列・文字リストについて で理解が深まったので追加しました
- あんまり使う機会に出会わなかったし、よくわかっていなかったので外していました
- あんまり使う機会に出会わなかったし、よくわかっていなかったので外していました
- シングルクォーテーション(')で囲むとListなんです!
- 詳しくは@im_miolabさんの記事をご参照ください!
iex> '123' |> length
3
iex> '123' |> Enum.count
3
MapSet
iex> map_set = MapSet.new([1,2,2,3,4,5,3])
# MapSet<[1, 2, 3, 4, 5]>
iex> map_set |> MapSet.size
5
iex> map_set |> Enum.count
5
iex> map_set |> length
** (ArgumentError) argument error
:erlang.length(#MapSet<[1, 2, 3, 4, 5]>)
Tuple
iex> {1, :two, "three"} |> tuple_size
3
iex> {1, :two, "three"} |> length
** (ArgumentError) argument error
:erlang.length({1, :two, "three"})
iex> {1, :two, "three"} |> Enum.count
** (Protocol.UndefinedError) protocol Enumerable not implemented for {1, :two, "three"} of type Tuple
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:153: Enumerable.count/1
(elixir) lib/enum.ex:600: Enum.count/1
Stream
iex> Stream.iterate(0, &(&1 + 1)) |> Enum.count
- 結果は得られません
バイナリ
iex> <<1,2,3>> |> byte_size
3
iex> <<1,2,3>> |> bit_size
24
iex> <<1,2,3>> |> length
** (ArgumentError) argument error
:erlang.length(<<1, 2, 3>>)
iex> <<1,2,3>> |> Enum.count
** (Protocol.UndefinedError) protocol Enumerable not implemented for <<1, 2, 3>> of type BitString
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:153: Enumerable.count/1
(elixir) lib/enum.ex:600: Enum.count/1
lengthとsizeのElixirにおける意味について
-
gumi TECH BlogのElixir入門 16: プロトコル を読んで知りました
- ありがとうございます!
- 公式にもかいてあります!
-
length
は全部数え上げるから要素の数に比例して処理時間がかかる、size
はデータ構造にサイズをもっているので一定時間で長さを返せる そうです(正確な意味は上記の原文をお読みください)
Enum.count/1はどうなのでしょうか
@doc """
Returns the size of the `enumerable`.
## Examples
iex> Enum.count([1, 2, 3])
3
"""
@spec count(t) :: non_neg_integer
def count(enumerable) when is_list(enumerable) do
length(enumerable)
end
def count(enumerable) do
case Enumerable.count(enumerable) do
{:ok, value} when is_integer(value) ->
value
{:error, module} ->
enumerable |> module.reduce({:cont, 0}, fn _, acc -> {:cont, acc + 1} end) |> elem(1)
end
end
-
List
だったら、Kernel.length/1 呼ぶ -
Enumerable.count/1
で、{:ok, 整数}
だったら、その整数を返す -
Enumerable.count/1
で、{:error, module}
だったら、module.reduce/3
で数え上げる - ということをやっているようです
iex(3)> is_list([1,2,3])
true # Enum.count([1,2,3])はこの結果によって、length([1,2,3])を呼び出して数え上げる
iex> Enumerable.count(%{hoge: 1, fuga: 2})
{:ok, 2} # Enum.count(%{hoge: 1, fuga: 2})はこの結果により、2を返す
iex> Enumerable.count(1..3)
{:ok, 3} # Enum.count(1..3)はこの結果により、3を返す
iex> '123' |> Enumerable.count
{:error, Enumerable.List} # Enum.count('123')はこの結果によってEnumerable.List.reduce/3で数え上げる
- データ型に応じて賢く長さを取得しているようです!