これまでのあらすじから。
- Perl で JS の arguments.callee 的なことしようと思ってハマった - IT戦記
- それDevel::Caller でできるよ - TokuLog 改め だまってコードを書けよハゲ
- 404 Blog Not Found:perl - で(Recall()|arguments.callee()|&?BLOCK())
- PerlでRecallの話 - Unknown::Programming
これ、それぞれに欠点がありました。Devel::CallerはXSに依存する。弾方式はソースフィルター、id:fbis方式はいちいち自分自身をスタックに積み直している...
この欠点を全て取り除く方法を発見しました。
[Run via codepad]
#!/usr/local/bin/perl
use strict;
use warnings;
our $CALLEE = 'whatever'; # just a place holder
sub recsub(&) {
my $code = shift;
sub {
local *CALLEE = \$code;
$code->(@_);
}
}
my $fact = recsub { $_[0] <= 1 ? 1 : $_[0] * $CALLEE->($_[0]-1) };
my $fib = recsub { $_[0] <= 1 ? 1 : $CALLEE->($_[0]-2) + $CALLEE->($_[0]-1)};
warn $fact->($_) for ( 1 .. 10 );
warn $fib->($_) for ( 1 .. 10 );
warn $CALLEE;
XSに依存しません。ソースフィルターにかけません。スタックに余計な物は積みません。
で、よろこびいさんで CPANize しようと思ったら....先達がいました。
全く同じ発想です。$CALLEEでなくて$RECではありますが。
ちなみに、myではなくourを使っている理由は、モジュール化した際にexportすることを考えていたから。それがなければ
[Run via codepad]
my $CALLEE; # just a place holder
sub recsub(&) {
my $code = shift;
sub {
$CALLEE = $code;
my $ret = $code->(@_);
$CALLEE = undef;
$ret;
}
}
でも行けます。
Dan the Wheel Reinventing Perl Monger
その方が時間単価の低いアプリケーションプログラマーには丁度良い。