I'm making a framework for specifying processes which may involve choices. I've got it working where each choice is an island. I'd prefer that subchoices 'fork' the parent choice, so that all options are properly considered.
choose :one => lambda {
choose [a, b]
if a
raise "Illegal"
end
},
:two => ....
Currently, it would always choose 'a' (which taken by itself looks better) but causes problems further down. Action :one with option 'b' is never considered.
I've run across callcc (not portable to all Ruby implementations, from what I've read) and fibers (new in 1.9 and can't be assumed to be available) as things that might be convinced to work, but I'm not crazy about having two implementations, or about the black magic of either of them, really.
I ended up taking the easy way out and passing the remainder of the computation as a block. This became a little less painful when I saw a similarity to an existing structure. I'm just hoping the indents don't get out of line.
The real case is significantly more complicated - there are side effects, but they are contained in a versioned key-value store. I'm also enumerating all possibilities and choosing the best one, so it can't just stop on success.
You may want to look through the solutions to [this quiz][1] for ideas.
-- MarkusQ
[1]: http://www.rubyquiz.com/quiz70.html "this quiz"
PS I'm on my way to a presentation but I'll check back and offer more when I get back, if no one else has stepped up to the plate.
Back, as promised.
Here are a few more ideas:
But all things considered, I think your best answer would be to wrap the functionality you want in a class or function, implement it with callcc
, and then do version detection in or around the definition of this as needed so that the right implementation is used in the right version of ruby.
As requested, here's an example of what I meant by chaining the choices together with yields. A bare bones implementation might look something like this:
def choose_one_of_each(choices,results,&block)
if choices.empty?
yield results
else
c = choices.dup
var,val = c.shift
choose(val) { |v|
choose_one_of_each(c,results.update(var => v),&block)
}
end
end
def choose(options,&block)
case options
when Hash then choose_one_of_each options,{},&block
when Range then options.each { |item| yield item rescue nil }
else options.each { |item| yield item rescue nil }
end
end
And you'd use it like this (somewhat expanded from your example, to show how the parts interact):
a = 7
b = 'frog'
choose(
:one => [a,b],
:two => ['stay','go','punt'],
:three => {:how => ['in the car','in a boat','by magic'],:how_fast => 0..2 }
) do |choices|
raise "illegal" if choices[:one] == a
raise "You can't stay fast!" if choices[:two] == 'stay' and choices[:three][:how_fast] > 0
raise "You go that slow!" if choices[:two] == 'go' and choices[:three][:how_fast] < 1
print choices.inspect,"\n"
end
Which would produce something like this (because of the print):
{:three=>{:how=>"in the car", :how_fast=>0}, :one=>"frog", :two=>"stay"}
{:three=>{:how=>"in the car", :how_fast=>0}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in the car", :how_fast=>1}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in the car", :how_fast=>1}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in the car", :how_fast=>2}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in the car", :how_fast=>2}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in a boat", :how_fast=>0}, :one=>"frog", :two=>"stay"}
{:three=>{:how=>"in a boat", :how_fast=>0}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in a boat", :how_fast=>1}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in a boat", :how_fast=>1}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in a boat", :how_fast=>2}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in a boat", :how_fast=>2}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"by magic", :how_fast=>0}, :one=>"frog", :two=>"stay"}
{:three=>{:how=>"by magic", :how_fast=>0}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"by magic", :how_fast=>1}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"by magic", :how_fast=>1}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"by magic", :how_fast=>2}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"by magic", :how_fast=>2}, :one=>"frog", :two=>"punt"}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.