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 );

Replies are listed 'Best First'.
Re^2: Variable number of foreach loops (improving performance)
by tobyink (Canon) on Dec 11, 2013 at 11:47 UTC

    You'll gain a lot more speed rewriting it in XS and using the multicall interface to call $code.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name