2010-09-15 17 views
30

Diğer birçok dilde, catch ve throw ifadeleri, Ruby'de ifadelerin başlatılmasını, kurtarılmasını ve yükseltilmesini yapar. Ben bu iki tablolar ile yapabilirsiniz biliyorum:Ruby'de yakalamak ve atmak nasıl kullanılır?

catch :done do 
    puts "I'm done." 
end 

ve

if some_condition 
    throw :done 
end 

Ama ne için bu yararlıdır? Birisi bana Ruby'de hangi catch ve throw ifadelerinin kullanıldığına dair bir örnek verebilir mi?

+1

Olası kopyası: http://stackoverflow.com/questions/51021/what-is-the-differance-between-raising-exceptions-vs-throwing-exceptions-in-ruby – Shadwell

cevap

32

İç içe geçmiş döngülerden kurtulmak için bunu kullanabilirsiniz.

INFINITY = 1.0/0.0 
catch (:done) do 
    1.upto(INFINITY) do |i| 
    1.upto(INFINITY) do |j| 
     if some_condition 
     throw :done 
     end 
    end 
    end 
end 

Yukarıda bir ara deyim kullandıysanız, iç döngüden ayrılmış olur. Ancak, iç içe döngüden kurtulmak istiyorsanız, bu yakalama/atma gerçekten yardımcı olacaktır. Euler sorunlarından birini çözmek için here kullandım.

21

Sinatra ile tanışana kadar bir süre güzel bir örnek arıyorum. IMHO, Sinatra, catch için çok ilginç bir örnek kullanım sergiliyor.

Sinatra'da, halt'u kullanarak istediğiniz zaman immediately terminate a request yapabilirsiniz. durdurma olduğunda da durumunu belirtebilirsiniz

halt 

...

halt 410 

Ya vücut ...

halt 'this will be the body' 

Veya her ikisi ...

halt 401, 'go away!' 

Durma yöntemi implemented using throw'dur.

def halt(*response) 
    response = response.first if response.length == 1 
    throw :halt, response 
end 

ve caught byinvoke yöntemi.

Sinatra'da :halt'un birçok kullanımı vardır. Daha fazla örnek için kaynak kodunu okuyabilirsiniz.

+1

sadece merak ediyorum, bu nasıl daha iyi istisnalar kullanmaktan daha mı? – jsz

+2

Bir istisna benzeri sistemi kontrol akışı olarak kullanmak için daha zarif bir yol atın. –

+2

@jsz bir, daha hızlıdır - yığın çerçevenin "atılmış sembol" boyunca taşınması gerekmez ve nesne oluşturulmaz. Hafif doğrusal olmayan akış kontrolü. – mezis

1

özyinelemeli işlevleri kullanarak iç içe veri yapıları üzerinde etkili özyinelemeli algoritmalar yazmaya, size for döngüler kullanılarak düz verileri temel alarak hareket bir break veya erken return yazma iteratif algoritmalar nasıl kullanacağını benzer throw kullanabilirsiniz.Örneğin

aşağıdaki koşullardan biri yerine getirildiğinde doğrudur dönecek bir fonksiyon yazmaya Eğer pozitif tamsayılar bir liste varsa ve (nedense) istediğinizi varsayalım:

  • toplamı listedeki tüm elemanları, daha büyük 100

en de her zaman liste üzerinde tek bir kısa devre geçişte bu denetimi gerçekleştirmek istediğiniz varsayalım eğer eşit listedeki bazı elementtir reduce c yerine Tüm toplamı almak ve ayrı bir any? beşli aramak için çağrı.

Muhtemelen böyle biraz bazı kod yazmak istiyorum

(aslında, muhtemelen hayatında bir noktada bir dille böyle bir kod yazdım):

çoğu dilde
def test(list) 
    sum = 0 
    for i in list 
    sum += i 
    if i == 5 || sum > 100 
     return true 
    end 
    end 
    return false 
end 

, hiçbir yoktur özyinelemeli bir işlev kullanan özyinelemeli bir algoritmanın kopması için eşdeğer bir eşdeğerdir. Ruby'de olsa da var! yerine bir liste ve Unsurları 100 üzerinden bir beş ya da toplamını ihtiva olmadığını kontrol etmek isteyen, kısa süre 100'ün üzerinde bir beş ya da toplamı bir ağaç varsa ve onun ayrılması durumunda kontrol etmek istediğiniz içeren varsayalım - cevabı bildiğiniz anda geri dönüp dönüyoruz.

Bunu, throw/catch ile zarif bunu yapabilirsiniz:

def _test_recurse(sum_so_far, node) 
    if node.is_a? InternalNode 
    for child_node in node.children 
     sum_so_far = _test_recurse(sum_so_far, child_node) 
    end 
    return sum_so_far 
    else # node.is_a? Leaf 
    sum_so_far += node.value 
    if node.value == 5 
     throw :passes_test 
    elsif sum_so_far > 100 
     throw :passes_test 
    else 
     return sum_so_far 
    end 
    end 
end 

def test(tree)    
    catch (:passes_test) do 
    _test_recurse(0, tree) 
    return false 
    end 
    return true 
end 

burada throw :passes_test bir break gibi biraz davranır; Tüm çağrı yığınınızın en dıştaki _test aramasının altına atlamanıza izin verir. Diğer dillerde, bu amaç için istisnaları kötüye kullanarak ya da yinelemeli işlevi yinelemeyi durdurmak için söylemek için bazı dönüş kodlarını kullanarak yapabilirsiniz, ancak bu daha doğrudan ve daha basittir.

İlgili konular