Velocity Reviews > Ruby > [Puzzle] How to do parameter variation?

# [Puzzle] How to do parameter variation?

Gary Boone
Guest
Posts: n/a

 12-07-2006

Suppose you want to do several runs of a program or function, varying
the parameters each time. You start with a hash of parameter lists:

params = { speed=>[1,2,3], beta=>[0.1, 0.002, 0.4], x=>['a', 'b'] }

Given this set, you'll call the test function 18 times:

test_function( {speed=>1, beta=>0.1, x=>'a'} )
test_function( {speed=>1, beta=>0.1, x=>'b'} )
test_function( {speed=>1, beta=>0.002, x=>'a'} )
... etc

So the puzzle is how to take the original params list and generate a
list of all the combinations of the parameters? The resulting parameter
sets will be passed one at a time to the function as shown above.

Notes:
- I used hashes and lists to explain the problem. Maybe there's a better
way.
- You don't know how many parameters will be given.
- The number of values for each parameter is unknown in advance.
- The parameters have different types, as shown above. Each parameter's
values are all the same type, though.

--
Posted via http://www.ruby-forum.com/.

Gregory Seidman
Guest
Posts: n/a

 12-07-2006
On Thu, Dec 07, 2006 at 04:25:43PM +0900, Gary Boone wrote:
}
} Suppose you want to do several runs of a program or function, varying
} the parameters each time. You start with a hash of parameter lists:
}
} params = { speed=>[1,2,3], beta=>[0.1, 0.002, 0.4], x=>['a', 'b'] }
}
} Given this set, you'll call the test function 18 times:
}
} test_function( {speed=>1, beta=>0.1, x=>'a'} )
} test_function( {speed=>1, beta=>0.1, x=>'b'} )
} test_function( {speed=>1, beta=>0.002, x=>'a'} )
} ... etc
}
} So the puzzle is how to take the original params list and generate a
} list of all the combinations of the parameters? The resulting parameter
} sets will be passed one at a time to the function as shown above.
}
} Notes:
} - I used hashes and lists to explain the problem. Maybe there's a better
} way.
} - You don't know how many parameters will be given.
} - The number of values for each parameter is unknown in advance.
} - The parameters have different types, as shown above. Each parameter's
} values are all the same type, though.

params = { :speed => [1,2,3], :beta => [0.1, 0.002, 0.4], => ['a', 'b'] }

all = params.inject([{}]) do |perms,(k,list)|
perms.inject([]) do |expanded,base|
list.each do |v|
expanded << base.merge(k => v)
end
expanded
end
end

require 'yaml'
puts all.size
puts all.to_yaml

--Greg

James Edward Gray II
Guest
Posts: n/a

 12-07-2006
On Dec 7, 2006, at 1:25 AM, Gary Boone wrote:

>
> Suppose you want to do several runs of a program or function, varying
> the parameters each time. You start with a hash of parameter lists:
>
> params = { speed=>[1,2,3], beta=>[0.1, 0.002, 0.4], x=>['a',
> 'b'] }
>
> Given this set, you'll call the test function 18 times:
>
> test_function( {speed=>1, beta=>0.1, x=>'a'} )
> test_function( {speed=>1, beta=>0.1, x=>'b'} )
> test_function( {speed=>1, beta=>0.002, x=>'a'} )
> ... etc

See if this gives you some ideas:

#!/usr/bin/env ruby -w

def call_with(meth, params, selected = Hash.new)
params = params.dup
key = params.keys.sort_by { |k| k.to_s }.first
choices = params.delete(key)

choices.each do |choice|
selected = selected.merge(key => choice)
if params.empty?
send(meth, selected)
else
call_with(meth, params, selected)
end
end
end

def print_args(args)
p args
end

call_with rint_args, :speed => [1, 2, 3],
:beta => [0.1, 0.002, 0.4],
=> ["a", "b"]
# >> {:speed=>1, :beta=>0.1, =>"a"}
# >> {:speed=>1, :beta=>0.1, =>"b"}
# >> {:speed=>2, :beta=>0.1, =>"a"}
# >> {:speed=>2, :beta=>0.1, =>"b"}
# >> {:speed=>3, :beta=>0.1, =>"a"}
# >> {:speed=>3, :beta=>0.1, =>"b"}
# >> {:speed=>1, :beta=>0.002, =>"a"}
# >> {:speed=>1, :beta=>0.002, =>"b"}
# >> {:speed=>2, :beta=>0.002, =>"a"}
# >> {:speed=>2, :beta=>0.002, =>"b"}
# >> {:speed=>3, :beta=>0.002, =>"a"}
# >> {:speed=>3, :beta=>0.002, =>"b"}
# >> {:speed=>1, :beta=>0.4, =>"a"}
# >> {:speed=>1, :beta=>0.4, =>"b"}
# >> {:speed=>2, :beta=>0.4, =>"a"}
# >> {:speed=>2, :beta=>0.4, =>"b"}
# >> {:speed=>3, :beta=>0.4, =>"a"}
# >> {:speed=>3, :beta=>0.4, =>"b"}

James Edward Gray II

Gary Boone
Guest
Posts: n/a

 12-08-2006

Cool. While I study the others, here's my solution. Looks like it's
similar to Greg's.

--Gary

# expand_hash_lists
#
# note that accumulating lists must be passed in as the first argument
#
def expand_hash_lists(a1, a2)
a1.inject([]){|acc,f| a2.inject(acc){|a,b| a << [b]+
(f.is_a?(Hash)?[f]:f)}}
end

if __FILE__ == \$0

# tests of expand_hash_lists
ah=[{'a'=>1}, {'a'=>2}, {'a'=>3}]
bh=[{'b'=>21}, {'b'=>22}, {'b'=>23}]
ch=[{'c'=>31}, {'c'=>32}, {'c'=>33}]
dh=[{'d'=>41}, {'d'=>42}, {'d'=>43}]

abh = expand_hash_lists(ah, bh)
abch = expand_hash_lists(abh, ch)
p expand_hash_lists(abch, dh).inspect

p "----"
p [[{'a'=>1}], [{'b'=>2}]].inject{|acc, vl| expand_hash_lists(acc,
vl)}.inspect
p "----"
p [[{'a'=>1}, {'a'=>3}], [{'b'=>2}]].inject{|acc, vl|
expand_hash_lists(acc, vl)}.inspect
p "----"
p [[{'a'=>1}, {'a'=>3}], [{'b'=>2}, {'b'=>4}]].inject{|acc, vl|
expand_hash_lists(acc, vl)}.inspect
end

--
Posted via http://www.ruby-forum.com/.