http://qs321.pair.com?node_id=1066607


in reply to Variable number of foreach loops

Here are some attempts at improving performance, compared to the solutions already posted (of which BrowserUK's recursive solution was the fastest):

Branching solution

A trivial (but non-elegant) way to gain performance is to directly implement the nested loops for a few (presumably common) values of n, using if/elsif/else to branch between them, and only fall back to the general-case recursive implementation for higher values of n:

sub multi_foreach(&@) { my $code = shift; if (@_ == 1) { for my $i (@{ shift() }) { $code->( $i ); } } elsif (@_ == 2) { my ($a0, $a1) = @_; for my $i0 (@$a0) { for my $i1 (@$a1) { $code->( $i0, $i1 ); } } } elsif (@_ > 2) { my ($a0, $a1, $a2, @rest) = @_; for my $i0 (@$a0) { for my $i1 (@$a1) { for my $i2 (@$a2) { if (@rest) { &multi_foreach_recursive( $code, scalar @rest, @rest, $i0, $i1, $i2 ); } else { $code->( $i0, $i1, $i2 ); } } } } } } multi_foreach { say join ' ', @_ } \( @a, @b, @c );

eval solution

The logical generalization of the branching solution, would be to dynamically generate the nested foreach loops for arbitrary levels of nesting. This incurs an overhead for code-generation each time the algorithm is called (PS: caching could help), but once generated the actual looping code will run very fast. Thus this solution tends to be slower than the recursive solution for small inputs, but faster for large inputs (i.e. many big arrays):

sub multi_foreach(&@) { my $code = shift; my ($head, $inside, $tail) = ('', '$code->(', ')'); foreach (0..$#_) { $head .= "for my \$i$_ (\@{\$_[$_]}) { "; $inside .= "\$i$_, "; $tail .= ' }'; } eval( $head . $inside . $tail ); } multi_foreach { say join ' ', @_ } \( @a, @b, @c );