Perlで継続

30分プログラム、その49。404 Blog Not Found:perl - to goto or not to goto, that's the continuationによるとPerlでも"本当の継続"が使えるらしいので、試してみる。

この記事は、

  • CPSを使えば、Perlでも継続が使えるよ
  • でも、単純にCPSで書くと遅いよ
    • どうも"Perlだと末尾再帰の最適化はされない"と書かれているように読みとれるんだけど、勘違いだよね
  • goto &CODEを使えば、効率がいいCPSが書けるよ

という意味らしい。決して、call/ccが書ける、という意味ではない。

use strict;
use warnings;
sub ident{
    @_;
}
 
sub length_cps(&@){
    my ($k,$x,@xs) = @_;
    unless($x){
	$k->(0);
    }else{
	@_ = (sub{$k->(1+shift)},@xs);
	goto &length_cps;
#	&length_cps(sub{ $k->(1+shift) },@xs);
    }
}
 
sub fact_cps(&$){
    my ($k,$n) = @_;
    if($n == 0){
	$k->(1)
    }else{
	@_ = (sub{ $k->($n*shift)},$n-1);
	goto &fact_cps;
#	&fact_cps(sub{ $k->($n*shift) },$n-1);
    }
}
 
sub reverse_cps(&@){
    my ($k,$x,@xs) = @_;
    unless($x){
	$k->(());
    }else{
	@_ = (sub{ $k->(@_,$x)},@xs);
	goto &reverse_cps;
#	&reverse_cps(sub { $k->(@_,$x) } , @xs);
    }
}
 
print length_cps(\&ident,(1,2,3,4)),"\n";
print fact_cps(\&ident,5),"\n";
print reverse_cps(\&ident,(1,2,3,4)),"\n";
  • scalar(@_)でlengthは書けるよ、というツッコミは禁止
  • ベンチマークしないとよさがわからないね
  • $kをレキシカル変数にしなくて、はまった。ちゃんとuse strictはつけましょう