TL;DR

Let’s turn our iterative solution from A simplified iterative implementation of NestedLoops into an iterator.

It’s about time to turn the iterative solution for NestedLoops into an iterator. We will start from the optimized version found in Benchmarking simplified implementations of NestedLoops:

 1 sub nested_loops_iterative {
 2    my ($dims, $opts, $cb) = @_;
 3    return unless scalar @{$dims};
 4    ($opts, $cb) = ($cb, $opts) if ref($opts) eq 'CODE';
 5    my @indexes     = (-1);
 6    my @accumulator = (undef) x scalar @{$dims};
 7    while ((my $level = $#indexes) >= 0) {
 8       my $dimension = $dims->[$level];
 9       my $i         = ++$indexes[$level];    # advance in "this" slot
10       if ($i > $#{$dimension}) { pop @indexes }
11       else {
12          $accumulator[$level] = $dimension->[$i];
13          if   ($level == $#{$dims}) { $cb->(@accumulator) }
14          else                       { push @indexes, -1 }
15       }
16    } ## end while ((my $level = $#indexes...))
17    return;
18 } ## end sub nested_loops_iterative

It’s quite clear that @indexes and @accumulator will keep track of the state, and that instead of calling the $cb as in line 13 we will have “just” to return the contents of @accumulator:

 1 sub nested_loops_iterator {
 2    my ($dims, $opts, $cb, $accumulator) = @_;
 3    return unless scalar @{$dims};
 4    ($opts, $cb) = ($cb, $opts) if ref($opts) eq 'CODE';
 5    my @indexes     = (-1);
 6    my @accumulator = (undef) x scalar @{$dims};
 7    return sub {
 8       while ((my $level = $#indexes) >= 0) {
 9          my $dimension = $dims->[$level];
10          my $i         = ++$indexes[$level];
11          if ($i > $#{$dimension}) { pop @indexes }
12          else {
13             $accumulator[$level] = $dimension->[$i];
14             if   ($level == $#{$dims}) { return @accumulator }
15             else                       { push @indexes, -1 }
16          }
17       }
18       return;
19    }
20 }

As you can see, the change is quite minimal!

A full modulino is shown below:

Until next time… take care!