凜「なんか難しいところ当たっちゃったけど頑張って調べてきたにゃ」
希「ループ、けっこう大変なん?」
凜「Elixirにはループ用の構文は用意されてないにゃ。だから、再帰というのを使うんだよ」
defmodule Idol do
def favorite(thing, n) when n == 1 do
IO.puts thing
end
def favorite(thing, n) do
IO.puts thing
favorite thing, n - 1
end
end
Idol.favorite "ラーメン", 3
凜「結果は」
ラーメン
ラーメン
ラーメン
凜「にゃ」
海未「やっていることのわりに、込み入っているように見えますが・・・」
凜「favorite関数の第2引数がカウンタにゃ。で、favorite関数内でもう一回favorite関数を呼び出すときに1減らしてるんだけど・・・」
海未「favoriteからfavoriteを呼び出す、ですか」
凜「それを再帰と言うにゃ。favoriteの中でfavoriteを呼んでるから、呼ばれたfavoriteはまたfavoriteを呼んで、いつまでも処理が続くにゃ」
希「それで結果的に繰り返しになるわけやね」
凜「うん。けど、それだといつまでも終わらないから、カウンタを作って1づつ減らしてるんだよ」
海未「それで、最初のガード節に引っかかったらそれ以上呼び出さないわけですね」
凜「2人ともさすがにゃ~。凜、これ調べててずーっと頭がぐるぐるしてたにゃ」
凜「ちなみにこの再帰のしかたは末尾再帰といって、Elixirだと効率がよくなるんだって」
海未「末尾再帰、ですか・・・?」
凜「関数の最後の文が再帰呼び出しになってるから、末尾再帰。関数の途中で再帰呼び出しをすることもできるんだけど、書けるならこうやって末尾に書く方がいいにゃ」
希「それ、どうしてなん?」
凜「関数呼び出しって、呼び出しから戻ってきた後に処理を続けるために、実行状態をどこかに取っておかないといけないんだよ。その置き場をスタックっていうんだけど、再帰で100回処理実行したらスタックに100段たまることになるにゃ」
希「実行回数が増えるほどリソースどんどん食い潰してく感じ?」
凜「うん。でも末尾再帰だと、呼び出しから戻ってきても何もすることないから、実行状態って取っておかなくてもいいよね?」
希「そうやね。その呼び出しの実行結果を戻すだけやもんね」
凜「で、ここがElixirのすごいところで、末尾再帰で書くとそもそもスタックに積まないように最適化してくれるにゃ!」
海未「なるほど、それで効率がいい、ということなのですね」
凜「何万回再帰呼び出ししても、大丈夫にゃ!」
海未「今日の凜は(いつになく)かしこいですね」
希「絵里ちよりは確実にかしこいね」
凜「もうすぐスクフェスの凜イベントだもん、ちゃんとアピールしておかないとボーダー低かったら悲しいにゃ」
海未「あの、それ、前回の私・・・」
希「ちなみに今日は2015年8月19日や」
凜「みんな3枚獲りよろしくにゃ~っ!」
海未「次回ですが、ループと近いところで、コレクションに対する反復処理としてEnumerableとStreamを見てみましょう」