2010-11-17 21 views
1

我想在Ruby中的塊中指定規則(在這個例子中,規則定義是「好」還是「壞」)的DSL。下面是一個(非常簡化的版本),我想做的事:從塊返回 - 從塊內調用的方法?

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 

有什麼辦法,我可以定義方法goodbad,使該塊休息時間嗎?在上面的例子中,我想:

  • 檢查,如果some_condition是真實的,如果是,打出來的塊,並將它返回true
  • 檢查,如果some_other_condition是真實的,如果是這樣,打出來的塊,並將它返回true
  • 回報從塊假無條件如果我們仍然在它

即我要讓上面的代碼的行爲,就好像我有writt恩,像這樣的塊:

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

在好/壞方法的定義把break顯然是行不通的。有沒有其他方法可以達到我想要的效果,還是應該考慮採取一些完全不同的方法來實現這一目標?

+0

非常相關:http://innig.net/software/ ruby/closures-in-ruby.rb – 2010-11-17 18:39:17

回答

3

您可以在塊中引發異常並捕獲該異常。

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另一個例子,而不是raise/rescue(更新通過沿返回值)(感謝@jleedev!):

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' 
+1

這可能是'throw' /'catch'的更好的應用,因爲你正在使用它們來進行流量控制。 (或者電話/ cc。嗨。) – 2010-11-17 18:40:50

+0

@jleedev你就是這樣!自從我使用這些東西已經太久了。這是從免費的第一版[使用throw/catch的入門文檔](http://phrogz.net/ProgrammingRuby/frameset.html?content=http%3A//phrogz.net/ProgrammingRuby/tut_exceptions.html%23catchandthrow) Ruby編程 – Phrogz 2010-11-17 18:47:22

+0

感謝@Phrogz和@jleedev - 我想過使用加註,但有點被告知只有「非常糟糕的情況」(tm)纔有例外情況,但是有點嚇壞了) 但是'throw' /''抓住「的方式感覺很少有錯誤! – 2010-11-17 19:35:16