無名関数(クロージャ)
無名配列や無名ハッシュと同じように、無名の関数(サブルーチン)へのリファレンスも簡単に作成する事ができます。
無名で関数が作れると何が便利なんでしょうか。
例えば、微分のように返り値として関数を生成して返す関数(高階関数)を作りたい場合、関数を「値」として受け渡ししなければなりません。こういった場合、通常の名前付き関数宣言では実現できません。
無名の関数は「匿名関数」「クロージャ」「ラムダ式(これはちょっと違いますが)」などとも呼ばれ、Scheme や Haskell などの関数型プログラミング言語ではよく登場します。抽象化にとても便利な機能です。
無名関数へのリファレンスは sub を名前なしで使えば作る事ができます。関数定義を勉強した時に出てきましたね。
デリファレンスはもちろん頭に「&」をつけるだけです。または、矢印演算子「->」を使う事もできます。
コードを書いてみます。
# ref5.pl
my $funcref = sub { print "@_\n"; }; # 文末の「;」に注意
&$funcref("apple", "banana", "orange");
$funcref->("apple", "banana", "orange");
引数を print する無名関数のリファレンスを $funcref に代入しています。文ですので、文末のセミコロン(;)は必要です。注意してください。
後の2文は同じ意味ですね。矢印演算子を使ったほうが読みやすいでしょうか。
実行結果です。
apple banana orange apple banana orange
ではでは、次に応用として「関数を受け取って実行する関数」を考えてみます。
# ref6.pl
sub hellofunc {
my $func = shift;
return $func->("Hello!");
}
hellofunc( sub { print "@_\n"; } ); # (1)
hellofunc( sub { print "@_ World!\n"; } ); # (2)
my $decorator = sub {
return "***" . shift() . "***";
};
print hellofunc($decorator), "\n"; # (3)
関数 hellofunc は、引数として関数へのリファレンスを受け取り、受け取った関数に "Hello!" という文字列を渡してコールしています。そしてその返り値をそのまま返します。
(1)と(2)では無名関数のリファレンスを直接渡しています。(1)は引数をそのままprintする関数、(2)は引数の後ろに World! とつけてprintする関数を渡しています。
(3)では引数の前後に***を付けた文字列を返す関数を渡しています。この場合、一旦変数に入れているので、名前が付いた関数を使ってるのと、あまり変わりませんね。
以下が実行結果です。
Hello! Hello! World! ***Hello!***