2015-04-02 13 views
5
def foo 
    puts "in foo" 
    s = yield 
    puts "s = #{s}" 
    return 2 
ensure 
    puts "in ensure" 
    return 1 
end 

def bar 
    foo do 
    puts "in bar block" 
    return 3 
    end 
    return 4 
end 


[36] pry(main)> r = bar 
in foo 
in bar block 
in ensure 
=> 4 

Spodziewałbym się r = 3, ale okazało się, że jest to r = 4. Jeśli usunę kod zabezpieczający, oczekiwane jest r = 3. Dlaczego to jest?Zwrot ruby ​​w bloku bloku wywoływanym z metody z zapewnieniem

def foo 
    puts "in foo" 
    s = yield 
    puts "s = #{s}" 
    return 2 
end 

r = bar 
in foo 
in bar block 
=> 3 
+1

Ten [blog] (http://railsware.com/blog/2012/11/20/yield-gotcha-in-ruby-blocks/) próbuje ci wyjaśnić ... co się dzieje ... –

Odpowiedz

3

Jest to funkcja Ruby polegająca na "rozwijaniu stosu" z bloków. Jak Twój zwrot działa krok po kroku:

  1. Powracasz 3 z bloku bar. return_value = 3, a Ruby oznacza, że ​​jest wartością zwracaną z bloku, więc powinien rozwinąć stos i powrócić 3 z funkcji nadrzędnej. W ogóle nie powróciłby do foo, gdyby nie było sekcji ensure. Jest to bardzo ważna różnica między powrotem z funkcji i bloków.
  2. Ale Ruby zawsze wykonuje ensure, a jest jeszcze jedna sekcja return w ensure z foo.
  3. Po return 1 w ensure części foo, return_value oznacza 1. Nie jest to wartość z bloku tak Ruby „zapomina” o poprzednich return 3 i zapomni zwrócić go z bar.
  4. Zwraca 1 z foo i 4 z bar.

Ponadto, jeśli piszesz next 3 zamiast return 3 w bloku - powróci do foo po yield i wykonać puts "s = #{s}"; return 2 nawet bez bloku zapewnienia. Jest to magiczna funkcja Ruby dla iteratorów i enumeratorów.