Warum tut explizite zurückkehren, machen Sie einen Unterschied in einem Proc?
-
07-07-2019 - |
Frage
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"
Ich dachte, die return
Stichwort wurde in Ruby optional und dass Sie immer return
ing, ob Sie es verlangen oder nicht.Da finde ich es verwunderlich, dass foo
und bar
unterschiedliche output-bestimmt durch die Tatsache, dass foo
enthält eine explizite return
in Proc f
.
Weiß jemand, warum dies der Fall ist?
Lösung
Ruby hat drei Konstrukte:
- Ein block ist kein Objekt und erstellt von
{
...}
oderdo
...end
. - Ein proc ist ein
Proc
Objekt erstellt vonProc.new
oderproc
. - Ein lambda ist ein
Proc
erstellt vonlambda
(oderproc
in Ruby 1.8).
Ruby hat drei Schlüsselwörter, die Rückkehr von etwas:
return
beendet die Methode oder ein lambda-Ausdruck es ist.next
beendet den block, proc, oder lambda-Ausdruck es ist.break
beendet die Methode ergab, dass der block aufgerufen oder das proc-oder lambda-Ausdruck es ist.
In lambdas, return
verhält sich wie next
, aus welchem Grund auch immer. next
und break
benannt sind wie Sie sind, weil Sie werden am häufigsten verwendet, mit Methoden wie each
, wo die Beendigung der block wird verursachen die iteration fortsetzen mit der weiter element der Sammlung und beenden each
führen, dass Sie Pause aus der Schleife.
Wenn Sie
return
innerhalb der definition von foo
, Sie wird von foo
, auch wenn es in einem block oder einer proc.Die Rückkehr aus einem block, den Sie verwenden können die next
Stichwort statt.
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"
Andere Tipps
Dies ist die Semantik für Proc
s; es ist nicht unbedingt die Semantik für alle Blöcke. Ich bin damit einverstanden dies ein wenig verwirrend ist. Es ist dort für zusätzliche Flexibilität (und vielleicht teilweise verursacht Rubin keine spec mit Ausnahme deren Umsetzung hat).
Das Verhalten wird in der Proc
Implementierung definiert. Lambda
s verhalten sich anders, wenn Sie also Ihre return
s möchten nicht verlassen aus dem einschließenden Verfahren, verwenden lambdas . Oder lassen Sie das return
Stichwort aus Ihrem Proc
.
Eine tiefe Untersuchung von Rubys Schließungen ist hier . Es ist ein fantastisches Exposé.
So:
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)
Denken auf diese Weise davon: Proc.new nur einen Block von Code erstellen, der Teil der Aufruffunktion ist. proc / Lambda schafft eine anonyme Funktion, die besonderen Bindungen hat. Ein wenig Codebeispiele helfen:
def foo
f = Proc.new { return "return from foo from inside Proc.new" }
f.call # control leaves foo here
return "return from foo"
end
entspricht
def foo
begin
return "return from foo from inside begin/end" }
end
return "return from foo"
end
so ist es klar, dass die Rückkehr kehrt gerade von der Funktion ‚foo‘
Im Gegensatz:
def foo
f = proc { return "return from foo from inside proc" }
f.call # control stasy in foo here
return "return from foo"
end
entspricht (die Bindungen zu ignorieren, da in diesem Beispiel nicht verwendet):
def unonymous_proc
return "return from foo from inside proc"
end
def foo
unonymous_proc()
return "return from foo"
end
, die als klar wird bestätigt von foo ist und stattdessen auf die nächste Anweisung fortgesetzt werden.