Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot

comment on

( #3333=superdoc: print w/replies, xml ) Need Help??

Non-recursive solution

For completeness, here's a solution that does not use a recursive subroutine:

sub nested_foreach(&@) { my $code = shift; my @indices = map { 0 } @_; # First set of indices is all zeroes my @sizes = map { scalar @$_ } @_; # Cache array sizes (optional) my $k; do { # Determine the array elements corresponding to the current set # of indices, and pass them to the closure: $code->( map { $_[$_][$indices[$_]] } 0..$#_ ); # Determine the next set of indices: for ($k = $#_; $k >= 0; $k--) { $indices[$k]++; if ($indices[$k] < $sizes[$k]) { last; } else { $indices[$k] = 0; } } # If $k went out-of-bounds, there are no more valid iterations: } while ($k >= 0); } my @a = ...; my @b = ...; my @c = ...; nested_foreach { say join ' ', @_ } \@a, \@b, \@c;

The "Determine the next set of indices" step may seem a little complicated at first sight, but it becomes more intuitive if you think of the @indices array as an integer number (with each element representing a digit), and imagine that we want to "increment" that "number" by 1. It's not a decimal (i.e. base-10) number, but rather one where each digit can have a different base (i.e. the sizes of the input arrays) - but that doesn't really change anything.

Incrementing the "number" by 1 works just like the integer addition (here with an addend of 1) that you were taught back in primary school: Start with the right-most digit; increment it; if it's still within the valid range of digits then you're done; if instead it went above the limit then wrap it around to zero, "carry the one", and repeat the same steps with the next digit to the left.


Performance comparison

Interestingly, my iterative solution seems to be significantly slower than BrowserUK's recursive solution, at least when running on my PC and with various different numbers/sizes of input arrays I tried:

sub nested_foreach(&@) { ... # see above } sub nForX(&@) { ... # see BrowserUK's post } # my @size = (500, 900); # my @size = (5, 5, 5, 5, 5, 5, 5, 5); my @size = (100, 4, 75, 23); my @AoA = map { [map { chr($_+64) x int(rand(10)) } 1 .. $_] } @size; cmpthese -10, { iterative => sub { nested_foreach { join("", @_) } @AoA }, recursive => sub { nForX { join("", @_) } scalar @AoA, @AoA }, };
s/iter iterative recursive iterative 1.86 -- -71% recursive 0.532 249% --

In reply to Re: Variable number of foreach loops (non-recursive solution) by smls
in thread Variable number of foreach loops by abhay180

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or or How to display code and escape characters are good places to start.
Log In?

What's my password?
Create A New User
Domain Nodelet?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2022-05-23 12:09 GMT
Find Nodes?
    Voting Booth?
    Do you prefer to work remotely?

    Results (82 votes). Check out past polls.