4. usage loop/recur
たとえばfibonacchi数を求めるコード片
loop y doで、1変数の再帰ループで、初期値はyであることを示している。
loop y do
0 -> 0 cond doパターンと同様にパターンマッチで記述。
yが0なら0, 1なら1,それ以外のxなら、という形で記述。
1 -> 1
x -> recur(x-1) + recur(x-2)
end loopの再帰を行う際のパラメータを指定する。
関数を定義する事無く再帰している。so, cool!!!
5. Impact of deprecate loop/
recur
loop/recurがelixir-0.6.0でdeprecatedとなり、0.7.0ではspecial formか
ら取り除かれることになった。
遅いから
special formは可変個の引数を持てたり、オーバライドされなかっ
たり、特別なスコープを持ったりした特別なものなので、数を減
らしたい。
関数定義すればいいじゃん
うわーん
6. loop/recur, do it yourself!
loop/recurがない場合、どうするか?
等価なコードはこんな感じ(fibonacchi数を例にとると)
f = fn(f, e) -> case e do
0->0
1->1
e -> f.(f, e-1) + f.(f, e-2)
end end
f.(f, y)
自分でloop/recurを実装してみる。そのためのマクロだw
7. mloop/mrecur
マクロによるloop/recurをmloop/mrecurとする
構造は、mloop x, do: blockとなっている。block中に、パターンマッ
チを書き、再帰の指示をmrecur(param)として示すことになる。
上記mloop x, do: blockの展開コードは、以下のようになってほしい。
mblock = block中のrecur(arg)をf.(f,arg)に書き換えたものとする。
f = fn(f, x) do mblock end
f.(f, x)
10. traversal elixir tree
行きがけ順に構文木タプルをなぞり、パターンを認識したら、置き換えるようにす
ればいい。
def traverse(a, f) do
r = f.(a) ## <-- パターンを認識して置き換える関数
cond do
is_tuple(r) -> list_to_tuple(traverse(tuple_to_list(r), f))
is_list(r) -> :lists.map(fn(x) -> traverse(x, f) end, r)
true -> r
end
end
11. build mloop
mb = traverse(block, fn(x) -> case x do
{:mrecur, line, args} when is_list(args) -> ## パターン
{{:”.”, line, [{:mrecur, line, :quoted}]},
line, [{:mrecur, line, :quoted}|args]}
_ -> x
end end)
12. build mloop 2
defmacro mloop(p, block) do
mb = traverse(block,...) # ブロックをトラバースしてパターン置き換え
quote do
mrecur = fn(mrecur, x) -> # mrecurという名前の変数で無名関数を作成
case(x, unquote(mb)) # 置き換えたblock
end
mrecur.(mrecur, unquote(p)) # loop pのp
end
end