計算機科学を学んでいる人にとっては今更な話題らしいですが、経済学部出身の自分には目新しい話題なので書いてみます。(といっても、全然知らなかった、というわけでもないんですが。)
id:amachang さんのエントリにある最終形のみを Perl で書くとこんな感じかな。
#!perl
my $Y = sub {
my $f = shift;
return sub {
my $g = shift;
return sub {
my $m = shift;
return $f->($g->($g))->($m);
}
}->(
sub {
my $g = shift;
return sub {
my $m = shift;
return $f->($g->($g))->($m);
};
}
)
};
my $fib = sub {
my $f = shift;
return sub {
my $n = shift;
return $n <= 2 ? 1 : $f->($n - 1) + $f->($n - 2);
};
};
warn $Y->($fib)->(10);
なぜこれで再帰として動作するかを考えるにあたって、id:gotin さんのエントリと同内容のことを、Perl で考えてみる。
簡単にするために最終形の Y コンビネータになる前の中間形のコードで考える。
#!perl
my $Y = sub {
my $f = shift;
my $g;
return $g = sub {
my $n = shift;
$f->($g)->($n);
}
};
my $fib = sub {
my $f = shift;
return sub {
my $n = shift;
return $n <= 2 ? 1 : $f->($n - 1) + $f->($n - 2);
}
};
warn $Y->($fib)->(5);
$Y->($fib) は、 my $Y = sub { ... } において、$f に $fib が代入され、結果として以下の関数が返ることになる。
#!perl
sub {
my $n = shift;
$fib->($g)->($n);
}
そうなると $Y->($fib)->(5) は、以下のように変換できる。
#!perl
$fib->($g)->(5)
$fib->($g) は、my $fib = sub { ... } の $f に $g が代入されるので、$fib->($g)->(5) は、
#!perl
sub {
my $n = shift;
return $n <= 2 ? 1 : $g->($n - 1) + $g->($n - 2);
}->(5);
ということになる。
なのでこの $n に 5 が代入され、結果として $fib->($g)->(5) は以下のように変換される。
#!perl
$g->(4) ... [A]
+ $g->(3) ... [B]
[A] を更に変換してみると、$g->(4) は $fib->($g)->(4) となり、
#!perl
sub {
my $n = shift;
return $n <= 2 ? 1 : $g->($n - 1) + $g->($n - 2);
}->(4);
を実行するのと同じ。というわけで、$g->(4) は、
#!perl
$g->(3) + $g->(2) ... [A]
になる。同様に [B] を変換すると、
#!perl
$g->(2) + $g->(1) ... [B]
となる。今までのところまでをまとめると、$Y->($fib)->(5) は以下のように変換される。
#!perl
$g->(3) + $g->(2) ... [A]
+ $g->(2) + $g->(1) ... [B]
で、$g->(3) は [B] の変換と同様なので、更に以下のように変換される。
#!perl
$g->(2) + $g->(1) + $g->(2) ... [A]
+ $g->(2) + $g->(1) ... [B]
ここで $g->(2) は、
#!perl
sub {
my $n = shift;
return $n <= 2 ? 1 : $g->($n - 1) + $g->($n - 2);
}->(2);
であり、$n <= 2 であれば 1 が返る。ということは、$g->(2) も $g->(1) も 1 だから、[A] + [B] は以下のように変換される。
#!perl
1 + 1 + 1 ... [A]
+ 1 + 1 ... [B]
というわけで、結果は 5 になる。ということらしい。なんとなくわかった。