晴耕雨読

working in the fields on fine days and reading books on rainy days

[Perl6] Map Filter Reduce in Raku

関数型言語の三種の神器である Map, Filter, Reduce を Perl6 (Raku) で行う方法について説明します。

Map: map

写像は map で行います。 map は関数なので、配列の値を直接操作することはしません。 もし破壊的な代入を望む場合は、map がミューテータとして振る舞うように . の代わりに .= を使います。

my @array = [1, 22/7, 42, 3.14];

say @array.map({ $_ * 2 });  # => (2 6.285714 84 6.28)
say @array;                  # => [1 3.142857 42 3.14]

say @array.=map({ $_ * 2 }); # => [2 6.285714 84 6.28]
say @array;                  # => [2 6.285714 84 6.28]

map の引数は関数なので、次のように書いても写像をすることができます。

sub double($a) { $a * 2 }
say @array.map(&double);
say @array.map: { $_ * 2 }
say @array.map(-> $a { $a * 2 });
say @array.map(* * 2);
say @array.map: * * 2;

もしメソッドチェインが嫌いな場合は、次のような関数的な書き方もできます。

say map { $_ * 2 }, @array;
say map({ $_ * 2 }, @array);

補足

写像の本質からは離れてしまいますが、引数である関数の引数が2つ以上の場合でも動作します。

my @array = [1, 2, 3, 4];
say @array.map: { $^a + $^b }
# => (3 7)

Filter: grep

選択は grep で行います。 grep は関数なので、配列の値を直接操作することはしません。 もし破壊的な代入を望む場合は、grep がミューテータとして振る舞うように . の代わりに .= を使います。

my @array = [1, 22/7, 42, 3.14];

say @array.grep({ $_ > 5 });  # => (42)
say @array;                   # => [1 3.142857 42 3.14]

say @array.=grep({ $_ > 5 }); # => [42]
say @array;                   # => [42]

grep の引数は関数なので、次のように書いても選択をすることができます。

sub filter($a) { $a > 5 }
say @array.grep(&filter);
say @array.grep: { $_ > 5 }
say @array.grep(-> $a { $a > 5 });
say @array.grep: -> $a { $a > 5 }
say @array.grep(* > 5);
say @array.grep: * > 5;

もしメソッドチェインが嫌いな場合は、次のような関数的な書き方もできます。

say grep { $_ > 5 }, @array;
say grep({ $_ > 5 }, @array);

Reduce: reduce

畳み込みは reduce で行います。

my @array = [1, 2, 3, 4];
say @array.reduce({ $^a + $^b });  # => 10

reduce の引数は関数なので、次のように書いても畳み込みをすることができます。

sub plus($a, $b) { $a + $b };
say @array.reduce(&plus);
say @array.reduce: { $^a + $^b }
say @array.reduce(-> $a, $b { $a + $b });
say @array.reduce(* + *);
say @array.reduce(&[+]);
say @array.reduce(&infix:<+>);

もしメソッドチェインが嫌いな場合は、次のような関数的な書き方もできます。

say reduce &[+], @array;  # => 10
say reduce(&[+], @array); # => 10

Reduction Operators を使うことによって、もっとスマートに畳み込むことができます。

say [+] @array;  # => 10

sub plus($a, $b) { $a + $b };
say [[&plus]] @array;  # => 10

以上です。