Elixirでいろいろな長さをはかる

# はじめに
- [Elixir](https://elixir-lang.org/)でいろいろな長さをはかってみたいとおもいます
- きっかけは[Map](https://hexdocs.pm/elixir/Map.html#content)の長さってどうやって調べるのだっけ? とおもったことがきっかけです
    - 長さが欲しくなることは実はあんまりなかったりする気もしていますが、いろいろ試してみたこと書いておきます

## この記事での[Elixir](https://elixir-lang.org/)のバージョン
```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](https://hexdocs.pm/elixir/Enum.html#count/1) 便利！
- 以下具体例です

# [List](https://hexdocs.pm/elixir/List.html#content)

```Elixir
iex> [1, 2, 3] |> length
3
iex> [1, [2, 3], 4, 5] |> Enum.count
4
```

- [Kernel.length/1](https://hexdocs.pm/elixir/Kernel.html#length/1)
- [Enum.count/1](https://hexdocs.pm/elixir/Enum.html#count/1)

# [Keyword](https://hexdocs.pm/elixir/Keyword.html#content)
```Elixir
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
```

- [Kernel.length/1](https://hexdocs.pm/elixir/Kernel.html#length/1)
- [Enum.count/1](https://hexdocs.pm/elixir/Enum.html#count/1)

# [Map](https://hexdocs.pm/elixir/Map.html)
```Elixir
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})
```

- [Kernel.map_size/1](https://hexdocs.pm/elixir/Kernel.html#map_size/1)
- [Enum.count/1](https://hexdocs.pm/elixir/Enum.html#count/1)

# [Range](https://hexdocs.pm/elixir/Range.html#content)
```Elixir
iex> 1..3 |> Enum.count
3
iex> 1..3 |> length
** (ArgumentError) argument error
    :erlang.length(1..3)
```

- [Enum.count/1](https://hexdocs.pm/elixir/Enum.html#count/1)

# 文字列
```Elixir
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
```

- [String.length/1](https://hexdocs.pm/elixir/String.html#length/1)
- [Kernel.byte_size/1](https://hexdocs.pm/elixir/Kernel.html#byte_size/1)

# 文字リスト
- [@im_miolab](https://qiita.com/im_miolab)さんの[Elixirで整数リストが「謎の文字」として返ってくる現象と、文字列・文字リストについて](https://qiita.com/im_miolab/items/2d41b10ff005b334295d) で理解が深まったので追加しました
    - あんまり使う機会に出会わなかったし、よくわかっていなかったので外していました:sweat:
- シングルクォーテーション(')で囲むとListなんです！
    - 詳しくは[@im_miolab](https://qiita.com/im_miolab)さんの記事をご参照ください！

```Elixir
iex> '123' |> length
3
iex> '123' |> Enum.count
3
```

- [Kernel.length/1](https://hexdocs.pm/elixir/Kernel.html#length/1)
- [Enum.count/1](https://hexdocs.pm/elixir/Enum.html#count/1)



# [MapSet](https://hexdocs.pm/elixir/MapSet.html#content)
```Elixir
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]>)
```

- [MapSet.size/1](https://hexdocs.pm/elixir/MapSet.html#size/1)
- [Enum.count/1](https://hexdocs.pm/elixir/Enum.html#count/1)

# [Tuple](https://hexdocs.pm/elixir/Tuple.html#content)
```Elixir
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
```

- [Kernel.tuple_size/1](https://hexdocs.pm/elixir/Kernel.html#tuple_size/1)

# [Stream](https://hexdocs.pm/elixir/Stream.html#content)
```Elixir
iex> Stream.iterate(0, &(&1 + 1)) |> Enum.count
```

- **結果は得られません**

# バイナリ
```Elixir
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
```

- [Kernel.byte_size/1](https://hexdocs.pm/elixir/Kernel.html#byte_size/1)
- [Kernel.bit_size/1](https://hexdocs.pm/elixir/Kernel.html#bit_size/1)

# lengthとsizeのElixirにおける意味について
- [gumi TECH Blog](https://dev.to/gumi)の[Elixir入門 16: プロトコル](https://dev.to/gumi/elixir-16--lif) を読んで知りました
    - ありがとうございます！
- 公式にもかいてあります！
    - [length and size](https://hexdocs.pm/elixir/naming-conventions.html#length-and-size) 
    - [Protocols](https://elixir-lang.org/getting-started/protocols.html)
- `length`は全部数え上げるから要素の数に比例して処理時間がかかる、`size`はデータ構造にサイズをもっているので一定時間で長さを返せる そうです(正確な意味は上記の原文をお読みください)

## Enum.count/1はどうなのでしょうか
- https://github.com/elixir-lang/elixir/blob/v1.9.4/lib/elixir/lib/enum.ex#L585-L607

```Elixir
  @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](https://hexdocs.pm/elixir/Kernel.html#length/1) 呼ぶ
- `Enumerable.count/1`で、`{:ok, 整数}`だったら、その整数を返す
- `Enumerable.count/1`で、`{:error, module}`だったら、`module.reduce/3` で数え上げる
- ということをやっているようです

```Elixir
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で数え上げる
```
- データ型に応じて賢く長さを取得しているようです！