# This says that I will be taking 3 named parameters # that can appear in any order, but 1 is required (++) sub NestedLoop (++@loop, +$OnlyWhen, +$code) returns Ref { # We start with all 0s (arrays) except the last wheel # This is so the first turn is in the correct position my @pos = 0 xx (@loop.elems - 1), -1; # We are creating an iterator so we return a code ref return sub { # We need to collect the values of the arrays # Since we are counting in indices my @group; # We added support for $OnlyWhen so each iteration # May actually be an unknown number of turns # So we loop until the condition is satisfied loop { # If in the process of turning the right most # wheel, we exceed the end - we turn a new wheel if ++@pos[-1] > @loop[-1].end { # We start at the nearest wheel to the right # and work to the left looking for 1 that # can be turned without exceeding the end for reverse 0 .. @pos.end - 1 -> $i { next if @pos[$i] == @loop[$i].end; # We turn that wheel and reset all the # wheels to the right to their star pos ++@pos[$i]; @pos = (@pos[0..$i], 0 xx (@pos.end - $i)) and last; } } # If we were not able to reset the right most # Wheel we must have not been able to move the # Left most wheel, which means we are done return () if @pos[-1] > @loop[-1].end; # We populate the @group with the actual values # Since we were just keeping track of indices @group = map -> $i { @loop[$i][@pos[$i]] } 0 .. @pos.end; # Since $OnlyWhen is optional, see if it is a # code ref execute it and loop if it doesn't # return a true value if $OnlyWhen.does(Code) { $OnlyWhen(@group) or next } # Execute the optional $code ref if it was defined $code(@group) if $code.does(Code); # End the infinite loop last; } # Return the values for this iteration of the loop return @group; }; };