in reply to NestedLoops (Algorithm::Loops) and Iterators
NestedLoops can't do it. (Correction: NestedLoops can if you pass it tied arrays.) But one could write NestedIters:
Algorithm/Iters.pm:
# # Algorithm/Iters.pm # use strict; use warnings; use Algorithm::Loops (); use Exporter (); package Algorithm::Iters; BEGIN { our $VERSION = '1.2.0'; our @EXPORT_OK = qw( NestedIters ); *import = \&Exporter::import; } # Import helpers: *_Croak = \&Algorithm::Loops::_Croak; *_Type = \&Algorithm::Loops::_Type; *isa = \&UNIVERSAL::isa; sub _NI_Args { my $loops= shift(@_); isa( $loops, "ARRAY" ) or _Croak( "First argument must be an array reference,", " not ", _Type($loops) ); my $n= 1; for my $loop ( @$loops ) { if( ! isa( $loop, "ARRAY" ) && ! isa( $loop, "CODE" ) ) { _Croak( "Invalid type for loop $n specification (", _Type($loop), ")" ); } $n++; } my( $opts )= @_; if( isa( $opts, "HASH" ) ) { shift @_; } else { $opts= {}; } my $code; if( 0 == @_ ) { $code= 0; } elsif( 1 != @_ ) { _Croak( "Too many arguments" ); } else { $code= pop @_; isa($code,"CODE") or _Croak( "Expected CODE reference not ", _Type($code) ); } my $when= delete($opts->{OnlyWhen}) || sub { @_ == @$loops }; if( keys %$opts ) { _Croak( "Unrecognized option(s): ", join ' ', keys %$opts ); } return( $loops, $code, $when ); } sub _NI_Iter { my( $loops, $code, $when )= @_; return sub { return } if ! @$loops; my @loops; my @iters; my @list; my $i= -1; foreach (@$loops) { if( isa( $_, 'CODE' ) ) { push(@loops, $_); } else { my @arr = @$_; push(@loops, sub { [ @arr ] }); } } return sub { while( 1 ) { # Prepare to append one more value: if( $i < $#loops ) { ++$i; local( $_ )= $list[-1]; $iters[$i]= $loops[$i]->(@list); if( isa( $iters[$i], 'ARRAY' ) ) { my @arr = @{$iters[$i]}; $iters[$i] = sub { pop @arr }; } } # Increment furthest value, chopping if done there: my $val; while( 1 ) { $val = $iters[$i]->(); last if defined $val; pop @list; return if --$i < 0; } $list[$i]= $val; my $act; $act= !ref($when) ? $when : do { local( $_ )= $list[-1]; $when->(@list); }; return @list if $act; } }; } sub NestedIters { my( $loops, $code, $when )= _NI_Args( @_ ); my $iter= _NI_Iter( $loops, $code, $when ); if( ! $code ) { if( ! defined wantarray ) { _Croak( "Useless in void context", " when no code given" ); } return $iter; } my @ret; my @list; while( @list = $iter->() ) { @list= $code->( @list ); if( wantarray ) { push @ret, @list; } else { $ret[0] += @list; } } return wantarray ? @ret : ( $ret[0] || 0 ); } 1;
example.pl:
#!/usr/bin/perl # # example.pl # use strict; use warnings; use Algorithm::Iters qw( NestedIters ); sub returns_an_iter1 { my $i = 0; sub { $i == 3 ? undef : ++$i } } sub returns_an_iter2 { my @data = ('y', 'n'); sub { pop @data } } sub returns_an_iter3 { my $i = 0; sub { $i == 4 ? undef : ++$i * 7 } } sub print_values { local $, = ", "; local $\ = "\n"; print @_; } NestedIters( [ \&returns_an_iter1, \&returns_an_iter2, \&returns_an_iter3, ], \&print_values ); # Also works # ========== # # print("\n"); # # NestedIters( # [ # \&returns_an_iter1, # sub { [ 'y', 'n' ] }, # \&returns_an_iter3, # ], # \&print_values # ); # # print("\n"); # # NestedIters( # [ # \&returns_an_iter1, # [ 'y', 'n' ], # \&returns_an_iter3, # ], # \&print_values # );
output:
1, y, 7 1, y, 14 1, y, 21 1, y, 28 1, n, 7 1, n, 14 1, n, 21 1, n, 28 2, y, 7 2, y, 14 2, y, 21 2, y, 28 2, n, 7 2, n, 14 2, n, 21 2, n, 28 3, y, 7 3, y, 14 3, y, 21 3, y, 28 3, n, 7 3, n, 14 3, n, 21 3, n, 28
NestedIters is identical to NestedLoops. It does everything NestedLoops does, down to the return values and options. However, it has two extra features:
This code is a derivative work of Algorithm::Loops.
Update: The OnlyWhen option is now supported.
Update: The package is now more aptly named.
Update: NestedIters now supports subs that return array refs (and just plain array refs), making it drop-in replacement for NestedLoops. It will automatically create iterators from array refs.
|
|---|