Perl高階関数
高階関数とは、関数を引数としたり、関数を返したりするような関数のことなわけだけども、Perlで使う人が多いとは思わない。そもそもsubを関数定義じゃなくてクロージャ生成に使う人って言うのが少ない。だが、そんな人でもPerlにおいては二つクロージャを使う機会がある。それは、sortとmapだ。
sort関数もmap関数も、似たような構文になっている。
sort BLOCK LIST map BLOCK LIST
間違いやすいのは、BLOCKの後ろにはカンマ(",")がいらないことだ。カンマを書くとBLOCKもLISTの一部と解釈されてしまう。ややこしい。
で、先日のデスマーチであるハッシュリファレンスの配列に対して、いろいろなソートをする必要が出た。本来であれば、こういったものはSQLでORDER BYしてやるのが正しいのだけども、SQLの発行部分をいじくるのが面倒だったので(SQLのデバッグはやりたくない)、Perlでソートしてやることになった。ちなみに、データ構造自体は平たくて、
@array = ({id => 1, name => "sawa"}, {id => 2, name => "yuta"}....);
といったような感じになっていた。単にSQLでひいてきた表組みそのままの形になっているのだ。
だが、ソートする規則が常に一定というわけではなく、ある二つの整数変数の値によって決まる。$var1が1で$var2が1だったら、そのハッシュリファレンス中のnameでソートする、$var1が1で$var2が2だったらほげほげで・・・といったイメージだ。でも、常にソートするのだから、ifでケース分けをしてやるというのは面倒だ。っていうか、ソースが汚い。ということで、一つ関数をかませることにした。
sub sort_func{ $var1 = shift; $var2 = shift; [ [ sub {$a->{id} <=> $b->{id}} sub {$a->{name} cmp $b->{name}} ], [ sub {$b->{id} <=> $a->{id}} sub {$b->{name} cmp $a->{name}} ] ]->[$var1]->{$var2]; }
で、例えば
my $sort_func = sort_func($var1, $var2); my @return = sort $sort_func @array;
としてやればいいんだけども、ここで一つ困った。一旦sort_funcの返り値を変数で受け取ってやる必要があるのだ。本来であれば、
sort sort_func($var1, $var2) @array;
というように書きたいのだが、現在のPerlでは書けない(Perl6は不明)。構文エラーと怒られてしまう。で、いろいろと試してみたのだが、出来ないようだ。mapやsortの直後にある関数は、何度もその後ろにあるLISTに対して適用するものであって、決して先に評価するcall by value仕様ではないからだろう。
でも、これが出来ないと結構不便なんだよなあ・・・ということで、ちょっと考えてみた。例えば、次のような関数mysortを定義してやる。
sub mysort{ my $func = shift; sort $func @_; } my @return = mysort sort_func($var1, $var2),@array;
こうしてやれば、先に関数が評価されるので何の問題もない・・・が、まあ気持ち悪いといえばそうかもしれない。そもそも、このsortとmapの仕様が気持ち悪いというのがその根底にあるのだけど・・・・