我正在制定一个框架来指定可能涉及选择的流程。我已经让它工作了,每个选择都是一个孤岛。我更喜欢子选择“分叉”父选择,以便正确考虑所有选项。

choose :one => lambda {
    choose [a, b]
    if a
      raise "Illegal"
    end
  },
  :two => ....

目前,它总是选择“a”(它本身看起来更好),但会导致进一步的问题。行动:从不考虑带有选项“b”的行动。

我曾经遇到过 callcc (根据我读到的内容,不能移植到所有 Ruby 实现)和 Fiber (1.9 中的新功能,不能假设可用)作为可能被说服工作的东西,但我真的,我并不热衷于拥有两种实现,或者其中任何一种的黑魔法。


我最终采取了简单的方法,并将剩余的计算作为一个块传递。当我看到与现有结构的相似之处时,这变得不那么痛苦了。我只是希望缩进不要超出范围。

实际情况要复杂得多 - 存在副作用,但它们包含在版本化的键值存储中。我也会列举所有的可能性并选择最好的一个,所以它不能仅仅停留在成功上。

有帮助吗?

解决方案

回来了,如约而至。

这里还有一些想法:

  • 您可以将选择与yield 链接在一起以按顺序遍历排列。换句话说,choose 可以根据传递给它的选项构建一组嵌套迭代器,并且它们只会屈服于链中的下一个迭代器。退出封闭的块会让你立即回到收益之后;如果您需要更多(例如失败原因)你可以提出并拯救。
  • 三个“r”(救援、提高和重试)的时髦排列可能会做到这一点,同样的想法是选择是嵌套选项主体或将它们嵌入到嵌套结构中。
  • 如果选项便宜且无副作用,您可能需要考虑仅生成所有排列并迭代它们。
  • 如果它们不是没有副作用,您可能想尝试某种伪单子解决方案,在其中您可以为每个排列惰性地生成 lambda。
  • 或多或少等效(但偏离你最初的问题),你也许可以为它们分配一个索引(如果你可以确定每个选择的基数,则最简单,但在任何情况下都可以使用分段索引)并迭代索引。
  • Fibers 已向后移植到 1.8.x

但考虑到所有事情,我认为你最好的答案是将你想要的功能包装在一个类或函数中,用 callcc, ,然后根据需要在 this 的定义中或周围进行版本检测,以便在正确的 ruby​​ 版本中使用正确的实现。

其他提示

您可能希望通过解决方案[本测验] [1]的想法看。

- MarkusQ

[1]: http://www.rubyquiz.com/quiz70.html “本测验”

P.S。我对我的方式来呈现,但我会回来检查,并提供更多的时候我回来,如果没有其他人加紧板。

按照要求,这里是什么,我的收益率链接的选择在一起意味着一个例子。裸露的骨头实现可能是这个样子:

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

和你使用这样的(从你的例子有所扩大,说明如何使用部分进行交互):

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

这会产生这样的东西(因为打印的):

{: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"}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top