Perl6 のフィボナッチ数列生成についての解説
mattn ブログで紹介されている Perl6 のフィボナッチ数列が奇妙に見える人が多いようなので、まともな解説。
ref. http://mattn.kaoriya.net/software/lang/perl6/20151026144119.htm
フィボナッチ数列とは以下のような数列です。
1, 1, 2, 3, 5, 8, 13, 21, 34, 55
最初の2つの数字が 1, 1 でして、その後のものは直前2つの数字を足したものです。
よって、Perl5 で記述した場合、先頭10個のフィボナッチ数列を求めるには以下のようになります。
use v5.16.0;
sub fib {
state %memo; # 一応 memoize ぐらいはしておく
my $n = shift;
$memo{$n} //= do {
if ($n == 0 || $n == 1) {
1
} else {
fib($n-1) + fib($n-2);
}
};
}
sub fibs {
my $n = shift;
map { fib($_) } 0..$n;
}
say join(', ', fibs(10));
ですが、Perl6 では無限数列を扱う機能が充実しているため、こういったものは非常に単純に記述できます。
Perl6 での書き方を解説しましょう。
まず、先頭の数列を用意します。
1, 1
次に、続きは一つ前の項目と2つ前の項目の和であることを示します。
1, 1, -> $a, $b { $a + $b } ...*
ここで、-> $a, $b { $a + $b }
はラムダ式です。これは ES6 などのものと同じですね。
...*
というのは、数列が続くことを示し、*
は無限大を表します。こうすることで無限に続く数列を表すことができるのです。
Perl6 では、2項のラムダは * + *
のように省略できるのでこれは
1, 1, *+* ...*
と表記することができます。
ここから、先頭10個を取り出すには
my @a = (1, 1, *+* ...*);
say @a[0..9];
また、レンジの右辺は ^10
のようにすれば「10は含まない」つまり「0~9」のレンジを作ることができるので、以下のように表記できます。
my @a = (1, 1, *+* ...*);
say @a[^10];
これをまとめると以下のようになります。
say (1, 1, *+* ...*)[^10];
ま、そんな感じです。