それ内部イテレータ
うはは、無理矢理。コードを生成して、eval するなら、そりゃそっちのが速いよなぁ。
つうか、逆転の発想というより、そりゃ外部イテレータが内部イテレータに変わっているって話だ。いやまぁ、別にいいっちゃいいんだけど、内部イテレータ有りならもっと素直に書いてたな。こんなの。
use strict; use warnings; sub mesh_iterate { my ( $sub, @points ) = @_; my $base = pop @points; unless (@points) { for my $i (@$base) { $sub->($i); } } else { mesh_iterate( sub { for my $i (@$base) { $sub->( @_, $i ); } }, @points ); } } mesh_iterate( sub { printf "[%s]\n", join(', ', @_); }, map { [ 0 .. 3 ] } 0 .. 2 );
ついでに、H.I. さんに提示してもらったコードはこんな感じ。(細部のスタイルは勝手に変更)
use strict; use warnings; sub mesh_iterator { return sub { } unless @_; my $lasts = pop; my $itsub = mesh_iterator(@_); my ($idx, @butlast) = (-1, $itsub->()); return sub { ($idx, @butlast) = (0, $itsub->()) and @butlast || return() if ++$idx >= @$lasts; return @butlast, $lasts->[$idx]; }; } my $iter = mesh_iterator( ([0..9]) x 3 ); while (my @p = $iter->()) { printf "[%s]\n", join(', ', @p); }
おお、うまい。というか、素直に考えればこうか。でも、何やっているんだか瞬時にはわかんないすね。世の Perl 使いはこういうのも瞬時に理解できるんだろうか。
もうちょっと読みやすくするとすればこうかな。
use strict; use warnings; sub mesh_iterator { return sub { } unless @_; my $lasts = pop; my $itsub = mesh_iterator(@_); my ( $idx, @butlast ) = ( 0, $itsub->() ); return sub { if ( $idx < @$lasts ) { return @butlast, $lasts->[ $idx++ ]; } else { ( $idx, @butlast ) = ( 0, $itsub->() ); return @butlast, $lasts->[ $idx++ ] if @butlast; return; } }; } my $iter = mesh_iterator( ( [ 0 .. 9 ] ) x 3 ); while ( my @p = $iter->() ) { printf "[%s]\n", join( ', ', @p ); }
で、何気に
なお、Perl6 ではyieldもサポートされる予定です。
む、そうなのか。期待。