One way of looping over nested iterators is to combine them into a
single iterator that loops over their Cartesian product.
In A mini-language for sequences (part 1), I give the code for *seq_prod*, which does this
for sequences, which are similar to iterators.

Assuming that your iterators wrap around upon exhaustion, it is
easy to convert them into sequences. Then you can combine them with
*seq_prod* and iterate over the resulting sequence. If
you want to work with iterators, you can convert the resulting
sequence back into an iterator, or you can write an *iter_prod*
function and avoid sequences altogether. In the example code below, I
will take the first route.

Let us say that iterators are defined as follows:

`sub iter {
my $vals = \@_;
my $i = 0;
sub {
$i >= @$vals ? do { $i = 0; undef } : $vals->[$i++];
};
}
sub enumerate_iter {
local $Data::Dumper::Indent = 0;
local $Data::Dumper::Terse = 1;
my ($iter) = @_;
my $i = 0;
while (defined($_ = $iter->())) {
printf "%2d: (%s)\n", $i++, Dumper($_);
}
print "\n";
}
enumerate_iter( iter( qw[a b c] ) );
# 0: ('a')
# 1: ('b')
# 2: ('c')
`

Then, the following functions let us convert between iterators and
sequences.

`sub iter_to_seq {
my ($iter) = @_;
sub {
my $val = $iter->();
defined $val ? ($val) : ();
};
}
sub seq_to_iter {
my ($seq) = @_;
sub {
my @val = $seq->();
@val ? [@val] : undef;
};
}
enumerate_iter(
seq_to_iter( iter_to_seq( iter( qw[a b c] ) ) )
);
# 0: (['a'])
# 1: (['b'])
# 2: (['c'])
`

Note that we wrap sequences with array references in order to
convert them into iterators. (Thus the iterator-sequence-iterator
round trip is not equivalent to an identity function.)

Finally, with the following functions, we can combine iterators as
sequences and convert the resulting sequence back to an iterator. The
*iter_prod* function encapsulates the entire process.

`use List::Util qw( reduce );
sub seq_prod {
no warnings 'once';
reduce { seq_prod2($a,$b) } @_ ;
}
sub seq_prod2 {
my ($s, $t) = @_;
my @sval;
sub {
my @tval;
while ( !@sval || !(@tval = $t->()) ) {
return () unless @sval = $s->();
}
( @sval, @tval );
};
}
sub iter_prod {
seq_to_iter( seq_prod( map iter_to_seq($_), @_ ) );
}
`

The following example shows how *iter_prod* works.

`my $abcees = iter(qw(a b c));
my $xyzees = iter(qw(x y z));
enumerate_iter(
iter_prod( $abcees, $xyzees )
);
# 0: (['a','x'])
# 1: (['a','y'])
# 2: (['a','z'])
# 3: (['b','x'])
# 4: (['b','y'])
# 5: (['b','z'])
# 6: (['c','x'])
# 7: (['c','y'])
# 8: (['c','z'])
`

Now you can replace your nested loops with a single loop over
the iterator product.

`my $product = iter_prod( $abcees, $xyzees );
while (defined (my $vals = $product->())) {
my @vars = @$vals;
print "Got vars: @vars\n";
# do real work here
}
# Got vars: a x
# Got vars: a y
# Got vars: a z
# Got vars: b x
# Got vars: b y
# Got vars: b z
# Got vars: c x
# Got vars: c y
# Got vars: c z
`

I hope this helps.

Cheers,

Tom

Comment onRe: NestedLoops (Algorithm::Loops) and IteratorsSelectorDownloadCode