Buzzurlの中の人日記
Perl で手軽にイタレータっぽいことをやりたいんだが
うぅむ、呼出し元にて丸括弧の中に中括弧があるのが、みっともないなぁ。もっと美しい書き方は無いものか。で、dankogai氏のページを見た: 404 Blog Not Found:perl - for(1..1e10) と Iterator
というのを見て、プロトタイプ宣言してやれば map とかみたいにコードブロック渡せるんじゃないか?とおもってやってみたら無理だったという話。
多分、元々やりたいことはRubyのiteratorみたいに$sar->each {
my ($time, $p) = @_;
print ("$time : $p\n");
};
こんな風に書けるメソッドを書きたいということだろうと思う。
でも↓のbad_eachでは期待したようには動かない
勉強不足で知らなかったのだけど、コードブロックを使いたいときは最初の仮引数じゃないとダメらしい。
package Sar;
use strict;
sub new {
my $class = shift;
bless {
"time" => { "a" => "1", "b" => "2" }
}, $class;
}
#↓これはダメ。コードブロックは1番目の仮引数じゃないと
# my $sar = new Sar();
# $sar->bad_each {
# my ($time, $p) = @_;
# print ("$time : $p\n");
# };
# とは書けない。
#
# $sar->bad_each( sub {
# my ($time, $p) = @_;
# print ("$time : $p\n");
# } );
# でないとコンパイルエラー
sub bad_each ($&){
my ($this, $yield) = @_;
my %times = %{$this->{time}};
map{
&{$yield}($_, $times{$_})
} sort keys %times;
undef;
}
最初の仮引数じゃなきゃだめということは、メソッドとしては実装できないので関数でやるしかないっぽい。
組み込みのmap/grep/sortなんかとの整合性としては正しい仕様という気もするけれど。
# Perlでコードブロックを使いたいときは、
#メソッドじゃなくて関数として実装するしかない?
# my $sar = new Sar();
# Sar::good_each {
# my ($time, $p) = @_;
# print ("$time : $p\n");
# } $sar;
sub good_each (&$) {
my ($yield, $obj) = @_;
my %times = %{$obj->{time}};
map{
&{$yield}($_, $times{$_})
}sort keys %times;
undef;
}
1;
しかしコードブロックのあとに渡すのがリストじゃなくてオブジェクトなのは気持ち悪いことこの上ない。でもリストを渡そうとする場合、$sar->{time}を隠蔽できない。アクセサなりなんなり適切なアクセス手段を用意してやればいいのだけど、$sar->{time}の詳細を見せるんだったら eachメソッドなんていらないという話だろう。
とはいえ、Perlとしてはこうやるのが自然という気がする。(iyahayaさんは$sar->{time}を隠蔽したりしたいのだろうから、これは邪道だけど)
my $sar = new Sar();
my %times = %{$sar->{time}};
map {
print "$_ : $times{$_}\n"
}sort keys %times;
この記事にコメントする

