- Perlで関数の部分適用と合成 : (2006/10/28)
-
OcamlやHaskellにある関数の部分適用、Haskellの関数の合成のようなものをPerlでやってみる例
関数の合成はともかくとして、部分適用のようなものは高階関数ならではか
Experimental::Partial.pm
OCamlの関数には、部分適用を行う機能がある。
let foo = fun x y -> x + y;;
let bar = foo 3 in
print_int(bar 4);;
関数fooは2個の引数を受け取る関数に見えるが、引数を1つしか与えずに評価してしまっている。
しかしこの例にあげたような関数ではエラーにはならない。この場合は、残り1つの引数を受け取る関数を返す。
その関数に残る1個の引数を与えて、foo本来の評価結果を返すことになる。
これは、例の方法で定義された関数は、カリー化によって引数を一つずつ受け取り、残りの引数を受け取る関数を返しているから。
(最適化によって、複数の引数を一度に処理してるかもしれないけど)
ちなみに組を受け取るよう定義された関数では、このような部分適用は行われない。
では、これと同じことをPerlで再現してみたらどうなるだろうか?
カリー化というより、とりあえず部分適用に焦点を絞ってみた例
use Experimental::Partial;
my $proc = partial
(
sub{print sprintf("%s %s %s %s\n", @_);},
4
);
my $p1 = $proc->("a", "b");
my $p2 = $p1->("c2");
$p1->("c", "d");
$p2->("d2");
Perlの関数のarityの取得方法がわからなかったので
(プロトタイプ付きのグローバル関数ならprototypeで取得できなくはないが、無名関数では無理らしい)、
関数partialに関数オブジェクトと、要求する引数の数を渡す仕様にした。
結果は部分適用に対応した関数オブジェクト(クロージャと呼ぶべきか?)である。この関数オブジェクトは指定した数か、それ以上の引数を受け取らない限り、残りの引数を要求する関数オブジェクトを返す。
カリー化を行うのが本来のやりかただろうが、今回はカリー化は行っていない。
Experimental::Composition.pm
また、以下は関数の合成の例
use Experimental::Composition.pm;
my $sub1 = sub{
my $x = shift;
my $y = shift;
return ($x * $x) + ($y * $y);
};
my $sub3 = composition($sub1, sub{sqrt shift});
print $sub3->(3, 4);
print "\n";
compositionは、複数の関数オブジェクトを受け取り、
それらの関数を合成した関数を返す。
合成された関数を評価した場合、まず最初にcompositionの第一引数に渡された関数が評価される。この場合、最初の関数には、合成関数に渡された引数がそのまま渡る。
次にcompositionの第二引数に渡されされた関数が評価されるが、この関数に渡る引数は、最初に評価した関数の結果が与えられる。
もしcompositionに3つ目の関数が与えられていた場合、その関数には2つ目に与えられた関数の結果が渡る。
以降の関数も同様に、直前に評価した関数の結果を引数として受け取ることになる。