{
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