無名再帰関数
再帰を無名のままやりたい。
…
無名の自分をどうやって呼ぶんだ?
名前を付けてやればいい。
無名で名前が付けられるのはというと、仮引数しかない。
何かの関数の実引数としてわたしてやればいいんじゃない?
こんな感じ
def nanika(f) f # あれ? end
引数としてfが渡されてきたということは、fは定義済みでブラックボックス。
fの定義をしたいのに。
…
fにブロックを渡せるようにしておけばいいんじゃない?
で、fの定義でブロックを呼び出す。
つまり、fの定義(の一部)を実行時まで遅らせる、実行時に決定できるようにしておく。
def f(self, x) ... self(self, x-1) ... end
こんな感じ。仮引数のselfは、自分自身(f)が入ってくる予定だよ〜という気持ち。
そしたら、nanikaの中では
def nanika(f, x) f(f, x) end
としておけばいい。
たとえば、階乗だと、
def fact(self, x) if x == 0 1 else x * self(self, x-1) end end
などとしておく。
そして、nanikaにfを突っ込む。
nanika(fact, 2)
ばらしていくと、
nanika(fact, 2) ↓ fact(fact, 2) ↓ # if 2 == 0 # 1 # else # 2 * fact(fact, 2-1) # end ↓ 2 * fact(fact, 1) ↓ fact(fact, 1) ↓ # if 1 == 0 # 1 # else # 1 * fact(fact, 1-1) # end ↓ fact(fact, 0) ↓ # if 0 == 0 # 1 # else # (略) # end ↓ 1 ↓ 1 * 1 ↓ 2 * 1 * 1 => 2
めでたし。
nanikaもfactも見た目上、再帰で書かれていないから、無名にするのは簡単。
nanika ↓ Proc.new{|f, x| f(f, x) } fact ↓ Proc.new{|self, n| if n == 0 1 else self(self, n-1) end }
これをつなげればいい。
無名で自分を呼べました。
上に書いたコードや用語は適当です。下のコードは動くと思います。
# Anonymous fact10 = Proc.new{|f| Proc.new{|n| f.call(f, n) } }.call( Proc.new {|slf, m| # factorial if m == 0 1 else m * slf.call(slf, m-1) end } ).call(10) p fact10