高階関数perl(higher order perl)
高階関数
高階関数は関数を引数にしたり、戻り値にする関数のことです。
Javaではそういうことは出来ませんが、perlは普通に出来ます。
(Java8ラムダは高階関数というのだろうか???)
スクリプト言語は大体出来るのかなぁと思います。
基本
testという関数を作成します。
この関数は引数として関数のリファレンスを取り、そのまま実行します。
この関数にprintするだけの無名関数を与えて実行してみます。
higher_order_perl.pl
#!/usr/local/bin/perl use strict; use warnings; sub test { my $func = shift; $func->(); } test(sub {print "hello"});
実行。
# perl higher_order_perl.pl
hello
print文が表示されました。
grepを自作する
grepはこんな感じで、引数として評価する式とリストをとります。
grep {$_ % 2} @list
grepをエミュレートするmy_grepという関数を作ってみました。
sub my_grep { my ($func, $list) = @_; my @result; for (@$list) { push @result, $_ if ($func->($_)); } return @result; }
簡単な説明。
引数を2つ受け取りますが、スカラー変数で受け取ります。
これはperlでは引数は@_にまとめられてしまうため、リストを渡す場合には参照渡しをする必要があるからです。
sub my_grep { my ($func, $list) = @_; : : }
なので、$listはデリファレンスしてやる必要があります。
@$list
あとは、引数で受けたリストに対して、引数で受けた関数を実行して、
式の評価がtrueの場合は結果として登録し、falseの場合は登録しないようにするだけです。
my_grepの実行は、無名関数とリストのリファレンスを指定します。
my @nums = 1 .. 9; my @odd = my_grep(sub {$_[0] % 2}, \@nums);
確認用コード。
my_grep.pl
#!/usr/local/bin/perl use strict; use warnings; sub my_grep { my ($func, $list) = @_; my @result; for (@$list) { push @result, $_ if ($func->($_)); } return @result; } my @nums = 1 .. 9; my @odd = my_grep(sub {$_[0] % 2}, \@nums); print "before: @nums \n"; print "after: @odd \n";
実行。
# perl my_grep.pl before: 1 2 3 4 5 6 7 8 9 after: 1 3 5 7 9
ただし、呼び出しの方法が組み込みのgrepと違い、
無名関数と配列のリファレンスを渡しています。
my_grep(sub {$_[0] % 2}, \@nums);
下記の組み込みのgrepの様な呼び出しをしたい場合、
プロトタイプ宣言を使うと実現できます。
grep {$_ > 2} @list
prototypeによる引数の宣言
perlでは関数の引数にプロトタイプを宣言することで、引数の型を指定できます。
下記の様なプロトタイプ指定ができます。
- @, % : リストコンテキストを強制
- $ : スカラーコンテキストを強制
- & : 無名サブルーチンを要求
プロトタイプ宣言したmy_grepがこちら。
sub my_grep (&@) { my $func = shift; my @result; for (@_) { push @result, $_ if ($func->($_)); } return @result; }
簡単な説明。
引数を2つ受け取りますが、プロトタイプで無名サブルーチンとリストを強制しています。
sub my_grep (&@) { : }
引数で渡すリストは、リストコンテキストを強制しているので、
デリファレンスせずにそのまま使用できます。
for (@_) { : }
プロトタイプ版のmy_grepは組み込みのperlと同じ様に呼び出せます。
my @nums = 1 .. 9; my @odd = my_grep {$_[0] % 2} @nums;
確認用コード。
my_grep_prototype.pl
#!/usr/local/bin/perl use strict; use warnings; sub my_grep (&@) { my $func = shift; my @result; for (@_) { push @result, $_ if ($func->($_)); } return @result; } my @nums = 1 .. 9; my @odd = my_grep {$_[0] % 2} @nums; print "before: @nums \n"; print "after: @odd \n";
実行
# perl my_grep_prototype.pl before: 1 2 3 4 5 6 7 8 9 after: 1 3 5 7 9
mapも同様に作れます。
my_map_prototype.pl
#!/usr/local/bin/perl use strict; use warnings; sub my_map (&@) { my $func = shift; my @result; for (@_) { push @result, $func->($_); } return @result; } my @nums = 1 .. 9; my @double = my_map {$_[0] * 2} @nums; print "before: @nums \n"; print "after: @double \n";
実行
# perl my_map_prototype.pl before: 1 2 3 4 5 6 7 8 9 after: 2 4 6 8 10 12 14 16 18
終わり。