Ruby 从块返回-从块内调用的方法返回?

Ruby 从块返回-从块内调用的方法返回?,ruby,Ruby,我试图定义一个DSL,其中在Ruby中的块中指定了规则(为了本例,这些规则定义了某些东西是“好的”还是“坏的”)。以下是我想做的(非常简化的)版本: def test_block # Lots of other code is_good = yield # ... should give me true or false # Lots of other code end test_block do good if some_condition good if some_

我试图定义一个DSL,其中在Ruby中的块中指定了规则(为了本例,这些规则定义了某些东西是“好的”还是“坏的”)。以下是我想做的(非常简化的)版本:

def test_block
  # Lots of other code
  is_good = yield   # ... should give me true or false
  # Lots of other code
end

test_block do
  good if some_condition
  good if some_other_condition
  bad
end
我有没有办法定义使块中断的方法
good
bad
?在上面的示例中,我想:

  • 检查某个条件是否为真,如果为真,则跳出该块并使其返回真
  • 检查某些其他条件是否为真,如果为真,则跳出该块并使其返回真
  • 如果我们仍然在块中,则无条件地从块返回false
i、 e.我想让上面的代码表现得像我写的块一样:

result = test_block do
  break true if some_condition
  break true if some_other_condition
  break false
end

break
放在好/坏方法的定义中显然是行不通的。是否有其他方法可以达到我想要的结果,或者我应该考虑一些完全不同的方法来实现这一点?

您可以在块中引发异常并捕获该异常

module Tester
  class Breaker < Exception; end
  class GoodBreak < Breaker; end
  class BaadBreak < Breaker; end
end

def test_block(name)
  begin
    yield
  rescue Tester::Breaker=>e
    case e
      when Tester::GoodBreak then puts "All is well with #{name}"
      when Tester::BaadBreak then puts "BAD STUFF WITH #{name}"
      else raise
    end
  end
end

def good; raise Tester::GoodBreak; end
def bad;  raise Tester::BaadBreak; end

test_block('early out') do
  good if true
  good if puts("NEVER SEE THIS") || true
  bad
end

test_block('simple pass') do
  good if false
  good if puts("SEE THIS FROM PASS TEST") || true
  bad
end

test_block('final fail') do
  good if false
  good if puts("SEE THIS BUT PUTS IS NIL")
  bad
end

#=> All is well with early out
#=> SEE THIS FROM PASS TEST
#=> All is well with simple pass
#=> SEE THIS BUT PUTS IS NIL
#=> BAD STUFF WITH final fail

非常相关:这可能是
throw
/
catch
的一个更好的应用程序,因为您将它们用于流控制。(或致电/cc.Heh。)@jledev没错!我已经很久没用了。这是免费的Ruby编程第一版。谢谢@Phrogz和@jleedev-我曾想过使用raise,但在被告知异常只适用于“非常糟糕的情况”(tm)后,我有点害怕了。)但是,
抛出
/
捕获
的方法感觉不那么错误!
def test_block(name)
  result = catch(:good){ catch(:bad){ yield } }
  puts "Testing #{name} yielded '#{result}'", ""
end

def good; throw :good, :good; end
def bad;  throw :bad,  :bad;  end

test_block('early out') do
  good if true
  good if puts("NEVER SEE THIS") || true
  bad
end

test_block('simple pass') do
  good if false
  good if puts("SEE THIS FROM PASS TEST") || true
  bad
end

test_block('final fail') do
  good if false
  good if puts("SEE THIS BUT PUTS IS NIL")
  bad
end

#=> Testing early out yielded 'good'
#=> 
#=> SEE THIS FROM PASS TEST
#=> Testing simple pass yielded 'good'
#=> 
#=> SEE THIS BUT PUTS IS NIL
#=> Testing final fail yielded 'bad'