{ package Iterator::Product; sub new { my $pkg = shift; @_ > 0 or die "$pkg->new() needs at least one iterator!"; @_ > 1 or return shift; if ( @_ > 2 ) # reduce! { my $car = shift; @_ = ( $car, $pkg->new( @_ ) ); } my $self = bless [@_], $pkg; $self->reset; $self } sub reset { my $self = shift; $self->[0]->reset; $self->_reset_inner; $self } sub _reset_inner { my $self = shift; $self->[1]->reset; $self->[2] = [ $self->[0]->value ]; $self } sub value { my $self = shift; $self->is_exhausted and die "past end"; $self->[1]->is_exhausted and $self->_reset_inner; ( @{ $self->[2] }, $self->[1]->value, ) } sub is_exhausted { my $self = shift; $self->[0]->is_exhausted && $self->[1]->is_exhausted } } #### use List::Util qw( reduce ); my $nested_iterator = reduce { new Iterator::Product $a, $b } @sub_iterators; #### { package Iterator::Array; use Scalar::Util qw(reftype); sub new { my $self = bless {}, shift; (reftype($_[0])||'') eq 'ARRAY' or die "Iterator::Array->new($_[0]) - argument is not an array ref!"; $self->{'ar'} = shift; $self->reset; $self } sub reset { my $self = shift; $self->{'i'} = $[; $self } sub value { my $self = shift; $self->is_exhausted and die "past end"; $self->{'ar'}[ $self->{'i'}++ ] } sub is_exhausted { my $self = shift; $self->{'i'} > $#{ $self->{'ar'} } } } #### my $it = new Iterator::Product Iterator::Array->new( ['1 ',' 2 ',] ), Iterator::Array->new( ['4 ',' 5 ',] ), Iterator::Array->new( ['7 ',' 8 ',] ); while ( ! $it->is_exhausted ) { my @x = $it->value; print "@x\n"; } #### 1 4 7 1 4 8 1 5 7 1 5 8 2 4 7 2 4 8 2 5 7 2 5 8