简体   繁体   English

多次执行剩余的红宝石块

[英]Execute remainder of ruby block more than once

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. 目前,它总是选择“ a”(看起来更好),但会导致问题进一步恶化。 Action :one with option 'b' is never considered. 行动:永远不会考虑选项“ b”。

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. 我遇到过callcc(据我所读的内容,不能移植到所有Ruby实现中)和光纤(在1.9中是新增功能,不能假定可用),可以说服它工作,但我确实对拥有两个实现或其中任何一个的黑魔法并不疯狂。


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. 您可能需要浏览[this quiz] [1]的解决方案以获得想法。

-- MarkusQ -MarkusQ

[1]: http://www.rubyquiz.com/quiz70.html "this quiz" [1]: http : //www.rubyquiz.com/quiz70.html “此测验”

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. PS:我正在演讲的路上,但是如果没有其他人站出来,我会再检查并提供更多信息。

Back, as promised. 像承诺的那样回来。

Here are a few more ideas: 这里还有一些想法:

  • You could chain the choices together with yield to do an in order traversal of the permutations. 您可以将选项与yield链接在一起,以对排列进行有序遍历。 In other words, choose could build a set of nested iterators out of the options passed to it, and they would just yield to the next one in the chain. 换句话说,choose可以根据传递给它的选项构建一组嵌套的迭代器,它们只会屈服于链中的下一个迭代器。 Exiting the enclosed block puts you right back after the yield; 退出封闭的块将使您立即回到收益率之上; if you need more (eg failure reasons) you could raise and rescue. 如果您需要更多(例如,故障原因),则可以举手营救。
  • A funky arrangement of the three 'r's (rescue, raise, and retry) might do it, again with the idea that choose is nesting the option bodies or embedding them in a nested structure. 可以通过三个“ r”(抢救,加高和重试)的时髦排列来做到这一点,再一次,选择的想法就是嵌套期权主体或将其嵌入嵌套结构中。
  • If the options are cheap and side effect free, you might want to look at just producing all the permutations and iterating through them. 如果这些选项便宜且无副作用,则您可能希望仅考虑产生所有排列并对其进行迭代。
  • If they aren't side effect free, you may want to try some sort of pseudo-monad solution, where you lazily produce lambdas for each permutation. 如果它们不是无副作用的,则您可能需要尝试某种伪monad解决方案,在该解决方案中,您会为每个排列延迟生成lambda。
  • More or less equivalent (but straying ever farther from your initial question) you might be able to assign them an index (easiest if you could determine the cardinality of each choice, but possible with a segmented index in any case) and iterate through the indexes. 或多或少地等效(但离您的初始问题越来越远),您可以为它们分配一个索引(如果可以确定每个选项的基数,最简单,但是在任何情况下都可以使用分段索引),然后遍历索引。
  • Fibers have been backported to 1.8.x 光纤已反向移植到1.8.x

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. 但是考虑到所有问题,我认为最好的答案是将所需的功能包装在类或函数中,使用callcc实现它,然后根据需要在此定义中或周围进行版本检测,以便使用正确的实现。在正确版本的红宝石中。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM