【问题标题】:Execute remainder of ruby block more than once多次执行 ruby​​ 块的剩余部分
【发布时间】:2010-10-10 05:51:11
【问题描述】:

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

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

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

我遇到过 callcc(根据我所阅读的内容,不能移植到所有 Ruby 实现)和 fiber(1.9 中的新功能,不能假定可用),这些东西可能会被说服工作,但是我并不热衷于拥有两个实现,或者它们中的任何一个的黑魔法,真的。


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

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

【问题讨论】:

    标签: ruby continuations


    【解决方案1】:

    您可能需要查看 [this quiz][1] 的解决方案以获取想法。

    -- 马库斯Q

    [1]:http://www.rubyquiz.com/quiz70.html"这个测验"

    附:我正在去参加演讲的路上,但如果没有其他人参与进来,我会回来查看并提供更多信息。

    【讨论】:

    • 如果必须的话,我可以自己找出延续,我只是真的希望有一个更便携的解决方案。
    • +1 - Amb 类看起来已经实现了您正在寻找的解决方案。而且我没有看到 callcc 被弃用的迹象 - ruby-doc.org/core-1.9/classes/Continuation.html
    • 嗯...我一定是打了一篇过时的文章。它似乎在某些实现上不受支持,但可能不会破坏交易。
    • Kernel#callcc 正在滚动到一个界面更简洁的延续库中。但这确实意味着您必须考虑可移植性。
    【解决方案2】:

    如约而至。

    这里还有一些想法:

    • 您可以将选项与 yield 链接在一起,以按顺序遍历排列。换句话说,choose 可以从传递给它的选项中构建一组嵌套迭代器,它们只会让步给链中的下一个迭代器。退出封闭块会让您在收益后立即返回;如果您需要更多(例如失败原因),您可以提出和救援。
    • 三个 'r's(rescue、raise 和 retry)的时髦排列可能会做到这一点,同样的想法是,选择嵌套选项主体或将它们嵌入到嵌套结构中。
    • 如果选项便宜且无副作用,您可能希望只考虑生成所有排列并迭代它们。
    • 如果它们不是无副作用的,您可能想尝试某种伪单子解决方案,您可以在其中为每个排列懒惰地生成 lambda。
    • 或多或少等价(但与您最初的问题相去甚远)您也许可以为它们分配一个索引(如果您可以确定每个选择的基数,则最简单,但在任何情况下都可以使用分段索引)并迭代通过索引。
    • Fibers have been backported to 1.8.x

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

    【讨论】:

    • 我会接受这个,因为它回答了我提出的问题,但实际情况要复杂得多。我对“将选择与收益链接在一起”有点感兴趣;你能指点我的好文章吗?
    • @JustinLove 我找不到文章,但我会尝试在第二天左右在这里发布一个示例。
    • @Justine Love 例如,见下文。
    【解决方案3】:

    根据要求,这是我将选择与收益链接在一起的意思的示例。一个简单的实现可能看起来像这样:

    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"}
    

    【讨论】:

    • 嗯...看起来像 lispy 风格的迭代。非常酷,但在我的情况下,选择不容易简化为数据结构。
    猜你喜欢
    • 2014-10-11
    • 2011-04-03
    • 2011-11-06
    • 2013-10-26
    • 2018-03-31
    • 2020-05-10
    • 2017-11-18
    • 1970-01-01
    • 2022-10-05
    相关资源
    最近更新 更多