2009-09-16 7 views
40

Ho pensato che la parola chiave return fosse facoltativa in Ruby e che tu sia sempre return se lo richiedi o meno. Detto questo, trovo sorprendente che foo e bar abbiano una produzione diversa determinata dal fatto che foo contiene un numero return esplicito in Proc f.Perché il ritorno esplicito fa la differenza in un Proc?

Qualcuno sa perché questo è il caso?

risposta

63

rubino ha tre costrutti:

  1. Un blocco non è un oggetto e viene creato { ... } o do ... end.
  2. A proc è un oggetto Proc creato da Proc.new o proc.
  3. A lambda è un Proc creato da lambda (o proc in Ruby 1.8).

Ruby ha tre parole chiave che ritorno da qualcosa:.

  1. return termina il metodo o lambda è in
  2. next termina il blocco, proc, o lambda è in
  3. . break termina il metodo che ha restituito il blocco o ha invocato il proc o lambda in cui si trova.

In lambdas, return si comporta come next, per qualsiasi motivo. next e break sono chiamati come sono, perché sono più comunemente utilizzati con metodi come each, dove chiude il blocco causerà l'iterazione per riprendere con il prossima elemento della collezione, e che chiude each causerà di pausa fuori dal giro.


Se si utilizza return all'interno della definizione di foo, si torna da foo, anche se è all'interno di un blocco o un proc. Per tornare da un blocco, è possibile utilizzare la parola chiave next.

def foo 
    f = Proc.new { next "return from foo from inside proc" } 
    f.call # control leaves foo here 
    return "return from foo" 
end 
puts foo # prints "return from foo" 
+0

Modificato per menzionare anche il comportamento di lambda – sepp2k

+0

nota, nell'esempio se il prossimo è omesso, il comportamento rimane. –

+1

btw, penso che entrambi abbiamo fatto un ottimo lavoro rispondendo ad una domanda diversa :) sul perché, penso che solo Matz lo sa, molte delle cose che riguardano le chiusure violano il principio della sorpresa minima. –

12

Questa è la semantica per Proc s; non è necessariamente la semantica di tutti i blocchi. Sono d'accordo che questo è un po 'confuso. È lì per una maggiore flessibilità (e forse parzialmente perché Ruby non ha specifiche tranne che per la sua implementazione).

Il comportamento è definito nell'implementazione Proc. Lambda s si comportano in modo diverso, quindi se si desidera che il proprio return s non esca da fuori dal metodo di inclusione, utilizzare lambdas.In alternativa, omettere la parola chiave return dal tuo Proc.

Un'indagine approfondita sulle chiusure Rubys is here. È un'esposizione fantastica.

Quindi:

def foo 
    f = Proc.new { 
    p2 = Proc.new { return "inner proc"}; 
    p2.call 
    return "proc" 
    } 
    f.call 
    return "foo" 
end 

def foo2 
    result = Proc.new{"proc"}.call 
    "foo2 (proc result is: #{result})" 
end 

def bar 
    l = lambda { return "lambda" } 
    result = l.call 
    return "bar (lambda result is: #{result})" 
end 

puts foo 
# inner proc 
puts foo2 
# foo (proc result is: proc) 
puts bar 
# bar (lambda result is: lambda) 
+0

"Questa è la semantica per Procs, non è necessariamente la semantica per tutti i blocchi." È la semantica per le istanze di Proc create da Proc.new e tutti i blocchi "normali" (cioè i blocchi che non sono usati insieme con le parole chiave 'proc' o' lambda'). – sepp2k

+0

È vero, potrei aggiungere un esempio, ma penso che il mio esempio sia abbastanza complicato. –

+0

per qualche motivo i lambda convertiti in blocchi si comportano come se fossero sempre blocchi: '[1] .map e lambda {return" lambda "}' restituisce dalla funzione. È un bug in JRuby? –

6

pensare in questo modo: Proc.new basta creare un blocco di codice che fa parte della funzione chiamante. proc/lambda crea una funzione anonima con collegamenti speciali. Un po esempi di codice aiuterà:

def foo 
    f = Proc.new { return "return from foo from inside Proc.new" } 
    f.call # control leaves foo here 
    return "return from foo" 
end 

è equivalente a

def foo 
    begin 
    return "return from foo from inside begin/end" } 
    end 

    return "return from foo" 
end 

quindi è chiaro che il ritorno sarà solo tornare dalla funzione 'foo'

in contrasto:

def foo 
    f = proc { return "return from foo from inside proc" } 
    f.call # control stasy in foo here 
    return "return from foo" 
end 

equivale a (ignorando le associazioni poiché non utilizzate in questo esempio):

def unonymous_proc 
    return "return from foo from inside proc" 
end 

def foo 
    unonymous_proc() 
    return "return from foo" 
end 

Quale è chiaramente non verrà restituito da foo e continuare alla successiva istruzione invece.

+3

anche se questa è una vecchia domanda, si prega di notare che c'è una differenza tra Ruby 1.8.7 e 1.9.3 dove in quest'ultimo Kernel.proc si comporta come Proc.new anziché come lambda. – froginvasion

Problemi correlati