今日のElixirSchoolメモ3「Enum」

Elixir Schoolの学習メモです

https://elixirschool.com/ja/lessons/basics/enum

## Enumモジュールとは？

Enumモジュールは列挙型を操作するためのおよそ70個以上の関数を含んでいる。
タプルを除く全てのコレクションを列挙できる。

### `all?/2`

コレクションの要素に対して適用する関数を渡す。
コレクションの全体がtrueと評価されなければならず、そうでなければfalseが返される。

```elixir
iex(19)> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 3 end)
false
iex(20)> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 1 end)
true
```

### `any?`

少なくとも1つの要素がtrueと評価された場合にtrueを返す。

```elixir
iex(21)> Enum.any?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 5 end)
true
```

### `chunk_every`

コレクションを小さなグループに分割できる。

```elixir
iex(22)> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]
```

### `chunk_by`

要素数ではない他の何かでコレクションを分類したい場合に使う。

```elixir
iex(23)> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"]]
iex(24)> Enum.chunk_by(["one", "two", "three", "four", "five", "six", "seven"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six"], ["seven"]]
```

### `map_every`

n毎のアイテムに対して何かの処理をしたい場合に使う。
これは最初の要素は必ず処理される。

```elixir
iex(25)> Enum.map_every([1,2,3,4,5,6,7,8], 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8]
```

### `each`

新しい値を生成することなく、コレクションを反復する場合に使う。

```elixir
iex(26)> Enum.each(["one", "two", "three"], fn(s) -> IO.puts(s) end)
one
two
three
:ok
```

### `map`

関数を各要素に適用して新しいコレクションを生み出すときに使う。

```elixir
iex(27)> Enum.map([0,1,2,3], fn(x) -> x-1 end)
[-1, 0, 1, 2]
```

### `min`と`max`

第②引数には、コレクションが空だった場合の最小値or最大値が設定できる。

```elixir
iex(28)> Enum.max([5,3,0,0,-1])
5
iex(29)> Enum.max([], fn -> :bar end)
:bar
```

### `filter`

```elixir
iex(30)> Enum.filter([1,2,3,4], fn(x) -> rem(x,2) == 0 end)
[2, 4]
```

### `reduce`

要素を1つの値に畳み込む。
任意のアキュムレータを渡すことができる。アキュムレータが指定されない場合はコレクションの最初の要素が用いられる。

```elixir
iex(32)> Enum.reduce([1,2,3], 10, fn(x, acc) -> x + acc end)
16
iex(33)> Enum.reduce([1,2,3], fn(x, acc) -> x + acc end)
6
iex(34)> Enum.reduce(["a","b","c"], "1", fn(x, acc) -> x <> acc end)
"cba1"
```

### `sort`

`sort/1`はソートの順序を決めるためにErlangのTerm優先順位を使う。
https://www.erlang.org/doc/reference_manual/expressions.html#term-comparisons

```elixir
iex(35)> Enum.sort([5, 6, 1, 3, -1, 4])
[-1, 1, 3, 4, 5, 6]
iex(36)> Enum.sort([:foo, "bar", Enum, -1, 4])
[-1, 4, Enum, :foo, "bar"]
```

`sort/2`では順序を決める関数を渡すことができる。

```elixir
iex(39)> Enum.sort([%{:val => 4}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)
[%{val: 4}, %{val: 1}]
```

:descや:ascを渡すこともできる。

```elixir
iex(38)> Enum.sort([5, 6, 1, 3, -1, 4], :desc)
[6, 5, 4, 3, 1, -1]
```

### `uniq`と`uniq_by`

```elixir
iex(41)> Enum.uniq([1, 2, 3, 2, 1, 1, 1, 1, 1])
[1, 2, 3]
```

`uniq_by`はユニークかどうかを比較する関数を渡すことができる。

```elixir
iex(42)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 3}], fn coord -> coord.y end)
[%{x: 1, y: 1}, %{x: 3, y: 3}]
```

### キャプチャ演算子（&）を使用したEnum

Enumモジュール内の多くの関数は、渡されるEnum型のオブジェクトを処理するための引数として無名関数を取る。
これらの無名関数は、多くの場合、キャプチャ演算子（＆）を使用して省略形で記述されることが多い。

```elixir
Enum.map([1, 2, 3], fn number -> number + 3 end)
```

上記は、下記のように実装できる。
数値のリスト（[1, 2, 3]）の各要素をキャプチャし、マッピング関数を通過するときに各要素を&1に割り当てる。

```elixir
Enum.map([1, 2, 3], &(&1 + 3))
```

無名関数を変数に割り当て、Enum.map/2関数から呼び出すこともできる。

```elixir
plus_three = &(&1 + 3)
Enum.map([1, 2, 3], plus_three)
```

### 名前つき関数でのキャプチャ演算子の使用

```elixir
defmodule Adding do
  def plus_three(number), do: number + 3
end

Enum.map([1, 2, 3], &Adding.plus_three(&1))
```

変数を明示的にキャプチャせずに、名前つき関数を直接呼び出すことができる。

```elixir
Enum.map([1, 2, 3], &Adding.plus_three/1)
```