どうも、靖宗です。
今回のテーマはRecursion(再帰)です。
アルゴリズミックな感じがして「逃げろ!」という気がしないでもないんですが、チュートリアルでしばきまわされることは無いと信じて進みます。
Loops through recursion(再帰ループ?)
一般的に関数型言語ではループの書き方が違うそうで。しらなんだ。
例えば、C言語でのループは以下のようにかける。
for(i = 0; i < sizeof(array); i++) { array[i] = array[i] * 2; }
せやな。という感じである。
ただし、関数型言語は変数が変わっていくのを嫌うっぽい(間違ってたらすみません)ので、実行中にarrayとiが絶え間なく変わっていくのが気にくわない?
Elixirではループはこう書く。
defmodule Recursion do def print_multiple_times(msg, n) when n <= 1 do IO.puts msg end def print_multiple_times(msg, n) do IO.puts msg print_multiple_times(msg, n - 1) end end Recursion.print_multiple_times("Hello!", 3) # Hello! # Hello! # Hello!
引数n
で条件分岐するよう一個目の関数の宣言の所にgurad句が書かれている。
guard句に引っかからなければ、再帰の関数が呼び出される仕組み。
そのときにn
が1ずつへっていくので、そのうち再帰の関数が呼び出されなくなるという仕組みです。
たぶんcond
で書いても行けそう。やってみる。
defmodule Recursion do def print_multiple_times(msg, n) do cond do n <= 1 -> IO.puts msg true -> IO.puts msg print_multiple_times(msg, n - 1) end end end
PS > iex.bat .\math.exs Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Recursion.print_multiple_times("Hello!", 3) Hello! Hello! Hello! :ok
なんか変なアトムが返ってきてるけど、元の形でも返ってきてるのでとりあえず今は放置。
Reduce and map algorithms(リデュースとマップアルゴリズム)
並列化プログラミングの時に出てくるアレだと思います。(そこまでくわしくない)
とりあえずサンプルを見ていきます。
defmodule Math do def sum_list([head | tail], accumulator) do sum_list(tail, head + accumulator) end def sum_list([], accumulator) do accumulator end end IO.puts Math.sum_list([1, 2, 3], 0) #=> 6
総和を再帰で書くとこうなる、というお手本みたいなコード。
headを足していって最後のリストが空になったら総和を返すみたいな感じ。
こういうパターンをreduce algorithmと言うそうです。
足し算(引き算も同様)が出てきたので、かけ算は?
全ての値を二倍にする関数
defmodule Math do def double_each([head | tail]) do [head * 2 | double_each(tail)] end def double_each([]) do [] end end
[head * 2 | 関数(tail)] [head * 2 | [head * 2 | 関数(tail)]] [head * 2 | [head * 2 | [head * 2 | 関数(tail)]]] ... みたいなイメージ?
なるほど。総和の関数もそうだったのですが、リストの実装がうまいこと生きてきてる気がする。
こういうパターンをmap algorithmと言うそうです。いろんな言語にあるhoge.map(list)
みたいなイメージかな。
最後の方に「Enum使うと上の奴こう書けるで」ってありますが。次回に回します。
ぱっと見めっちゃ便利そう・・・