GuidoFubini has asked for the wisdom of the Perl Monks concerning the following question:

My apologies in advance for posting such an involved question right off the bat.

What I have: a hash of N arrays. Each array belongs to a parameter for a function, and contains the values that the parameter will take on. There are a small, finite number of possible paramters (say 20) and only a handful are used in the hash, but which specific parameters are used may vary from one time I run this to the next. I need to explore the entire parameter space at all the points given. So, a given instance of the hash is something like this:
my %params = ( x_pos => [1..10], y_pos => [-5..5], z_pos => [0..15], spin => [0, 1], neato => [1..10]);
And which parameters are included as well as what the ranges are will be decided prior to running.
Generally, I've been using a bunch of nested loops to get all the combinations of parameters:
foreach my $x_pos (@{$params{x_pos}}) { foreach my $y_pos (@{$params{y_pos}}) { . . . my $result = function($x_pos,$y_pos,...) . . . } }
All these loops are written by hand.

My Question: Is there a way to generalize these loops so that all combinations are used without knowing in advance which parameters or how many are in the hash? It should be noted that the parameters need to go in a specific order, e.g. that "spin" must be inside the "*_pos" loops.

Replies are listed 'Best First'.
Re: Methodically search an arbitrary space?
by Tanktalus (Canon) on May 05, 2006 at 16:46 UTC

    Looks like Loop Abstraction via Recursion may be what you're looking for.

    Note that you can't use a hash because, by its nature, it's unordered. If you had, however, a list of arrays you wanted to loop through, such as:

    my @params = ( [1..10], [-5..5], [0..15], [0,1], [1..10], );
    Then you could use the solutions in the above meditation, especially the one talking about Algorithm::Loops.

      Wow, that looks dead on. There's no reason that I used a hash in my example other than "arbitrary sized collection" makes me think of a hash.

      Thanks Tanktalus!
Re: Methodically search an arbitrary space?
by ikegami (Patriarch) on May 05, 2006 at 17:07 UTC

    Algorithm::Loops's NestedLoops handles this easily. I added extra arguments to function because function had no way of knowing what each argument was. (Was the first argument a x_pos? a y_pos? etc)

    use Algorithm::Loops qw( NestedLoops ); my %params = ( x_pos => [1..10], y_pos => [-5..5], z_pos => [0..15], spin => [0, 1], neato => [1..10], ); my @keys = keys %params; NestedLoops( [ @params{@keys} ], sub { function(map { $keys[$_], $_[$_] } 0..$#keys) } ); sub function { my %args = @_; print(join(', ', map { "$_ => $args{$_}" } keys %args), "\n"); }

    Update: You have since indicated you didn't care about the names, so the above simplifies to:

    use Algorithm::Loops qw( NestedLoops ); my @params = ( [1..10], [-5..5], [0..15], [0, 1], [1..10], ); NestedLoops(\@params, \&function); sub function { print(join(', ', @_), "\n"); }

    I bet you didn't expect the solution to be one line long :)

Re: Methodically search an arbitrary space?
by explorer (Chaplain) on May 05, 2006 at 17:34 UTC