Perché il ritorno esplicito fa la differenza in un Proc?
-
07-07-2019 - |
Domanda
def foo
f = Proc.new { return "return from foo from inside proc" }
f.call # control leaves foo here
return "return from foo"
end
def bar
b = Proc.new { "return from bar from inside proc" }
b.call # control leaves bar here
return "return from bar"
end
puts foo # prints "return from foo from inside proc"
puts bar # prints "return from bar"
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 un output diverso determinato dal fatto che foo
contiene un return in
Proc f
.
Qualcuno sa perché questo è il caso?
Soluzione
Ruby ha tre costrutti:
- Un blocco non è un oggetto ed è creato da
{
...}
odo
.. .end
. - Un proc è un oggetto
Proc
creato daProc.new
oproc
. - Un lambda è un
Proc
creato dalambda
(oproc
in Ruby 1.8).
Ruby ha tre parole chiave che ritornano da qualcosa:
-
return
termina il metodo o lambda in cui si trova. -
next
termina il blocco, proc o lambda in cui si trova. -
break
termina il metodo che ha ceduto al blocco o invocato il proc o lambda in cui si trova.
In lambdas, return
si comporta come next
, per qualsiasi motivo. next
e break
sono chiamati così come sono perché sono più comunemente usati con metodi come ciascuno
, dove terminare il blocco causerà l'iterazione riprendi con l'elemento successivo della raccolta e terminando ciascuno
ti farà interrompere fuori dal ciclo.
Se usi
return
all'interno della definizione di foo
, tornerai da foo
, anche se si trova all'interno di un blocco o di un proc. Per tornare da un blocco, puoi usare invece 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"
Altri suggerimenti
Questa è la semantica per Proc
; non è necessariamente la semantica per tutti i blocchi. Sono d'accordo che sia un po 'confuso. È lì per una maggiore flessibilità (e forse in parte perché Ruby non ha specifiche tranne la sua implementazione).
Il comportamento è definito nell'implementazione Proc
. I Lambda
si comportano diversamente, quindi se desideri che il return
s non esca dal metodo allegato, usa lambdas . In alternativa, ometti la parola chiave return
dal tuo Proc
.
Un'indagine approfondita sulle chiusure di Rubys è qui . È una fantastica esposizione & # 233 ;.
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)
Pensaci in questo modo: Proc.new crea semplicemente un blocco di codice che fa parte della funzione chiamante. proc / lambda crea una funzione anonima con collegamenti speciali. Alcuni esempi di codice aiuteranno:
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 tornerà dalla funzione 'pippo'
al contrario:
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 i binding poiché non utilizzati in questo esempio):
def unonymous_proc
return "return from foo from inside proc"
end
def foo
unonymous_proc()
return "return from foo"
end
Il che è chiaramente non tornerà da foo e continuerà invece alla prossima dichiarazione.