Elixir ~ Enum.reduce/3関数の整理

# はじめに
[`Enum.reduce/3`](https://hexdocs.pm/elixir/Enum.html#reduce/3)関数について、初期値と累積値の関係を改めて確認した結果を整理しました。今までちょっとあやふやな理解でしたが、`Enum.reduce/3`関数に対してしっかり理解できました。

# 書式
関数部を書くときは`fn x, acc -> func(x, acc)`のように各値:x と累積値:accの2つの引数を必ずとります。

# 結論

```elixir
result = Enum.reduce([x1, x2, x3..., xn], init, fn x, acc -> func(x, acc) end)
と書く時は以下が実行される。
ans1 = func(x1, init) # init: 指定したaccの初期値
ans2 = func(x2, ans1)
ans3 = func(x3, ans2)
  :
result = func(xn, ans(n-1))
```

[ElixirのEnum.reduceを一発で理解する方法](https://qiita.com/sand/items/e9003dcf045e75577833)の記事では以下のように記載されていました。

```
reduce [1,2,3,4..n] init fun = (fun n ... (fun 4 (fun 3 (fun 2 (fun 1 init)))) ...)
```

この書き方は最初は飲み込めませんでしたが、

```
acc = init
acc = fun 1 init
acc = fun 2 (fun 1 init)
 :
```
と伝搬されていくことがわかれば、理解しやすいと思います。

一方、[Elixir ~Enum.reduceを分解、理解しよう~](https://qiita.com/kmizuno0211/items/27d6120c94ac86bdcc00)の記事では以下のように記載されていました。

```elixir
init
|> M.func(1)
|> M.func(2)
|> M.func(3)
```

こちらの書き方はaccumulatorの伝搬という意味では理解しやすいと思いますが、accの値の変化という意味ではちょっとわかりづらいと感じました。

# ケース1
簡単なケース

```elixir
iex> Enum.reduce(1..3, "start", fn x, acc -> "#{acc} plus #{x}" end)
"start plus 1 plus 2 plus 3"
```

## 展開

```elixir
iex> "#{acc} plus #{x}"  <-- acc: "start"(指定した初期値), x: 1
"start plus 1"
iex> "#{acc} plus #{x}"  <-- acc: "start plus 1"(前回出力値) , x: 2
"start plus 1 plus 2"
iex> "#{acc} plus #{x}"  <-- acc: "start plus 1 plus 2"(前回出力値) , x: 3
"start plus 1 plus 2 plus 3"
```

ちなみに[`Enum.reduce/2`](https://hexdocs.pm/elixir/Enum.html#reduce/2)だと...

```elixir
iex> Enum.reduce(1..3, fn x, acc -> "#{acc} plus #{x}" end)
"1 plus 2 plus 3"
```
1回目の関数は実行されず、`acc = x1` が実行されるだけみたい。(hexdoc.pmにかかれてますね)

# ケース2
書籍プログラミングElixirのプロセスのオーバーヘッドでの`Chain.create_processes`関数

```elixir
defmodule Chain do
  def counter(pid) do
    receive do
      n -> send pid, n + 1
    end
  end
  def create_processes(n) do
    last = Enum.reduce (1..n, self(),
      fn _, send_to ->
        spawn(Chain, :counter, [send_to])
      end)
    send (last, 0)
  end
end
```

## 展開

```elixir
last = Enum.reduce(1..n, self(),
  fn _, send_to ->
    spawn(Chain, :counter, [sent_to])
  end)
は以下のような展開になります。

iex> pid1 = spawn(Chain, :counter, [self()])  # acc:self() 指定した初期値, x: 不使用
#PID<0.140.0>  (1例です)
iex> pid2 = spawn(Chain, :counter, [pid1])  # acc:pid1, x: 不使用
#PID<0.142.0>  (1例です)
iex> pid3 = spawn(Chain, :counter, [pid2])  # acc:pid2, x: 不使用
#PID<0.144.0>  (1例です)
  :
last = spawn(Chain, :coutner, [pid(n-1)])  # acc:pid(n-1), x: 不使用
#PID<0.150.0>  (1例です)
```

- enumerableを(1..n)とすることによって、n回の関数実行(プロセス生成)とする。
- enumerableの各値は関数内では使用していない。
- 各回の返り値(pid)をそのまま次の関数の引数とすることによって、pidに関連付けて連鎖的にプロセスを生成する。

ということになります。
`Enum.reduce/3`から脱線しますが、send (last, 0)にてメッセージを送ると、先ほどプロセスを生成した順序とは逆の方向にメッセージが連鎖的に伝搬されていきます。
![fig.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304912/8477cf25-8906-e5ac-f1ff-85fc3f05ba96.png)


# ケース3
mapの更新。
[Elixirでナンプレを解くコードを書いてみた](https://qiita.com/dimanche/items/38e4f345a206cc63d3c6)の記事にてmapの更新を以下のように書きました。

```elixir
Enum.reduce(getkey(board, n), option, fn x, acc -> Map.update!(acc, x, fn value -> value -- [intg] end) end)
```

ここでoptionは以下のようなmapです。

```elixir
option = %{
  "00" => [2, 3]
  "01" => [2, 6]
  "02" => [1, 7, 9]
  "03" => [2, 6, 7]
    :
  "88" => [5, 7]
}  #1例です。
```

getkey関数の返り値は、値の更新対象となるキーのリストです。

```elixir
getkey = ["10", "11", "12", "13", ..."79"]  #1例です。
```

## 展開

```elixir
Enum.reduce(["10", "11", "12", "13"], option, fn x, acc -> Map.update!(acc, x, fn value -> value -- [3] end) end)
は以下のような展開となります。 
iex> option1 = Map.update!(option , "10", Map.get(option , "10") -- [3])
%{"00" => []...}
iex> option2 = Map.update!(option1, "11", Map.get(option1, "11") -- [3])
%{"00" => []...}
iex> option3 = Map.update!(option2, "12", Map.get(option2, "12") -- [3])
%{"00" => []...}
iex> result  = Map.update!(option3, "13", Map.get(option1, "13") -- [3])
%{"00" => []...}
```

mapにて更新するキーのみの値をまとめて変更し、その他のキーの値を引き継ぐ書き方ができました。