Elixir ~Enum.reduceを分解、理解しよう~

#ここまでのあらまし

そろそろ最初にJavaに触れてプログラミング学習をはじめてから半年。
直後にElixirを志すことにしてドキュメントの写経や動作確認をするようになってからやはり半年弱。
北九州に移り住んで、音楽やデザインと並行しながら直接メンターの先生にElixirを教わるようになって2ヶ月ほど。

Elixirはじめプログラミングの学習効率が全く思うように上がらない、定着しない壁を感じていました。

そこで、これまでに書いた自分のドキュメントを振り返り、理解および理解できないことの認識を新たにすることにしました。

そして、ひょんなことからメンターの[山崎進先生](https://twitter.com/zacky1972) が、わたしとの学習に際してのやりとりをTwitter上で連続ツイートの形で公開されました。

今回は、それに即して今回わたしの疑問にあがったEnum.reduceの式を分解、理解していこうと思います！

#本題

今回あつかう式は以下です。


<blockquote class="twitter-tweet" data-lang="ja"><p lang="fr" dir="ltr">iex&gt; Enum.reduce([1, 2, 3], 0, fn x, acc -&gt; x + acc end)<br>6</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124232863145054209?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

```elixir
iex> Enum.reduce([1, 2, 3], 0, fn x, acc -> x + acc end)
6
```

最初、わたしの理解は第一引数のリストの総和を求める式、というものでした。

じゃあ第二引数のゼロは？第三引数はxとaccの和に見えるのになぜこれが総和を求める式になるのか？accとは？という感じでした。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">もうわかりましたね？<br><br>iex&gt; Enum.reduce([1, 2, 3], 0, fn x, acc -&gt; x + acc end)<br><br>は<br><br>1+2+3 = 6<br><br>を計算しているようです。</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124234364630396928?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

しかし、第一引数をいじるかぎりわたしのここまでの仮説、つまり第一引数のリストの数値の総和、という仮説は合っているように思えます。

続いて第二引数。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">では次に第2引数を変更して推測してみましょう。<br><br>iex&gt; Enum.reduce([1, 2, 3], 1, fn x, acc -&gt; x + acc end)<br><br>これは？<br><br>iex&gt; Enum.reduce([1, 2, 3], 2, fn x, acc -&gt; x + acc end)<br><br>こうだと？<br><br>iex&gt; Enum.reduce([1, 2, 3], -1, fn x, acc -&gt; x + acc end)<br><br>これならどうでしょう？</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124234749575196673?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 


それぞれ、

```elixir
iex> Enum.reduce([1,2,3],1, fn x,acc -> x + acc end)
7
iex> Enum.reduce([1,2,3],2, fn x,acc -> x + acc end)
8
iex> Enum.reduce([1,2,3],-1, fn x,acc -> x + acc end)
5
```

わたしは第一引数の総和に対して加算減算しているのではないか、と思いました。
しかしこれはEnum.reduceの本来の動きからすると正確ではなく、

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">わかりました？<br><br>実はさっき<br><br>iex&gt; Enum.reduce([1, 2, 3], 0, fn x, acc -&gt; x + acc end)<br><br>は<br><br>1+2+3 = 6<br><br>と言ったのですが，<br><br>より正確には<br><br>0 (第2引数) + 1 + 2 + 3 = 6<br><br>だったんです！</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124235332751245312?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

というのが正解でした。

関門である第三引数に移ります。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">iex&gt; Enum.reduce([1, 2, 3], 0, fn x, acc -&gt; x + acc end)<br><br>第3引数が無名関数を使っているので，初学者には理解しづらいかもしれません。次のように名前のある関数に直しておきますね。<br><br>defmodule M do<br> def func(x, acc) do<br> x + acc<br> end<br>end<br><br>Enum.reduce([1, 2, 3], 0, &amp; M.func(&amp;1, &amp;2))</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124236868629831680?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

```elixir
Enum.reduce([1,2,3],0, fn x,acc -> x + acc end)
```

を


```elixir
defmodule M do
	def func(x,acc) do
		x + acc
	end
end

Enum.reduce([1,2,3],0, & M.func(&1,&2))
```

と書き換えます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">実は Enum.reduce/3 は本当は次のような計算をしています。<br><br>M.func(3, M.func(2, M.func(1, 0)))<br><br>パイプライン演算子を使うと次の通りです。<br><br>0<br>|&gt; M.func(1)<br>|&gt; M.func(2)<br>|&gt; M.func(3)</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124237412861747200?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

```elixir

M.func(3, M.func(2, M.func(1, 0)))
```

を、外から読もうとしてうまく掴めなかったのですが、(1,0)の計算結果が次の第二引数になって〜という流れで、

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

の構造を理解することができました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">つまり Enum.reduce/3 は<br><br>第2引数を初期値として，パイプライン演算子で数珠つなぎに次々と，第1引数のリストの各要素を順番に取り出して第3引数の関数を適用し，第1リストの最終要素まで適用した累積結果を返す<br><br>ということなんです。</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124238396916883458?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 


つまり、
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">総和を求めるのではなく、累積して要素に関数を適用する、Enum.reduceの第二引数、つまり起点がゼロで、第三引数の式がxとacc(それまでの累積)の和だから、結果としてこの式は総和を求めているように見える式になる。</p>&mdash; Kento Mizuno (@kmizuno0211) <a href="https://twitter.com/kmizuno0211/status/1124242757978021888?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

ゼロを起点に、リストの各要素とそれまでの計算結果を累積して足していく。
自分の理解と日本語でのアウトプットをうまく繋げるのに骨が折れるのですが、こんなところでしょうか。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">「累積」という言葉が出ましたが，累積を英訳すると，accumulate<br>つまり Enum.reduce/3 に登場した acc なんです。</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124239508801589248?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

Enumの公式ドキュメントを和訳しても感じが掴みにくかったのですが、理解につなげることができました！

しかるに、
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">もし以上の連続ツイートを完全に理解できているならば，次のような関数を実装できると思います。<br><br>1 (第2引数) * 1 * 2 * 3 * 4 = 24<br><br>を計算する Enum.reduce/3 を使った式を書いてみてください。<br><br>以上が練習課題です。Let&#39;s challenge!</p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124239997811318784?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

この式を考えていくと、1を起点に累積してどんどん、こんどは乗法しているように見えます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ca" dir="ltr">iex &gt; Enum.reduce([1,2,3,4],1,fn x,acc -&gt; x * acc end)<br>24<br><br>です！</p>&mdash; Kento Mizuno (@kmizuno0211) <a href="https://twitter.com/kmizuno0211/status/1124243232928325632?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

```elixir

iex > Enum.reduce([1,2,3,4],1,fn x, acc -> x * acc end)
24
```

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">正解 <a href="https://t.co/3qEtzzLL7B">https://t.co/3qEtzzLL7B</a></p>&mdash; Susumu Yamazaki (@zacky1972) <a href="https://twitter.com/zacky1972/status/1124243661753962496?ref_src=twsrc%5Etfw">2019年5月3日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

やった！

ということになります。

#追記

練習問題の式を、&記法とパイプライン演算子で書き換えてみようと思います。

```elixir
iex > [1,2,3,4] |> Enum.reduce(1, & &1 * &2)
24
```

理解が固まったような気がします。

Elixirの学習効率の陰りについて、わたしは自分の発達障害や双極性障害、伴って起きている脳機能の障害じゃないか、と思い悶々としていました。
理解の礎が足りていないのではないかと思って、ほかの言語や教材に手を出しては、とっ散らかって余計パニックになってしまう有り様でした。
ようやく初学レベルにたどり着いたことと、おそらくはわたしへの指導に即して先生が公開ツイートの連投の形を取るこの学習法によって、なにかブレイクスルーが訪れないかなと思い、少なくともわたしはこれからにワクワクしています。

うまずたゆまず、頑張ります。
Kento Mizuno