61 Stimmen

Warum macht die explizite Rückgabe einen Unterschied in einem Proc?

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 Schlüsselwort in Ruby optional war und dass Sie immer return ob Sie es wünschen oder nicht. Angesichts dessen finde ich es überraschend, dass foo y bar haben eine unterschiedliche Leistung, die durch die Tatsache bestimmt wird, dass foo enthält eine ausdrückliche return sur Proc f .

Weiß jemand, warum dies der Fall ist?

105voto

sepp2k Punkte 352762

Ruby hat drei Konstrukte:

  1. A Block ist kein Objekt und wird erstellt durch { ... } o do ... end .
  2. A proc es un Proc Objekt erstellt von Proc.new o proc .
  3. A lambda es un Proc erstellt von lambda 又は proc in Ruby 1.8).

Ruby hat drei Schlüsselwörter, die von etwas zurückkehren:

  1. return beendet die Methode oder Lambda, in der es sich befindet.
  2. next beendet den Block, den Proc oder das Lambda, in dem er sich befindet.
  3. break beendet die Methode, die zum Block geführt hat oder den Proc oder Lambda aufgerufen hat, in dem sie sich befindet.

In Lambdas, return verhält sich wie next aus welchen Gründen auch immer. next y break sind so benannt, weil sie am häufigsten mit Methoden verwendet werden wie each , wobei das Beenden des Blocks dazu führt, dass die Iteration mit der nächste Element der Sammlung, und die Beendigung each wird Sie dazu bringen Pause aus der Schleife.


Wenn Sie return innerhalb der Definition von foo werden Sie zurückkehren von foo auch wenn sie sich innerhalb eines Blocks oder einer Prozedur befindet. Um aus einem Block zurückzukehren, können Sie die next stattdessen das Schlüsselwort.

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 Stimmen

Bearbeitet, um auch das Verhalten von Lambdas zu erwähnen

0 Stimmen

Beachten Sie, dass in Ihrem Beispiel, wenn next weggelassen wird, das Verhalten bestehen bleibt.

2 Stimmen

Btw, ich denke, wir haben beide eine gute Arbeit geleistet, indem wir eine andere Frage beantwortet haben :) warum, das weiß wohl nur Matz, verstößt vieles im Zusammenhang mit Verschlüssen gegen das Prinzip der geringsten Überraschung.

16voto

Sam Saffron Punkte 124121

Dies ist die Semantik für Proc s; das ist nicht unbedingt die Semantik für alle Blöcke. Ich stimme zu, dass dies ein wenig verwirrend ist. Es ist dort für zusätzliche Flexibilität (und vielleicht teilweise, weil Ruby hat keine Spezifikation außer für seine Implementierung).

Das Verhalten ist in der Proc Umsetzung. Lambda s verhalten sich unterschiedlich, wenn Sie also möchten, dass Ihr return s nicht zu verlassen aus der einschließenden Methode heraus, verwenden Sie lambdas . Oder lassen Sie das return Schlüsselwort aus Ihrem Proc .

Eine eingehende Untersuchung der Rubys-Verschlüsse ist hier . Es ist ein fantastisches Exposé.

Así que:

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 Stimmen

"Dies ist die Semantik für Procs, es ist nicht notwendigerweise die Semantik für alle Blöcke." Es ist die Semantik für Proc-Instanzen, die von Proc.new erzeugt werden, sowie für alle "normalen" Blöcke (d.h. Blöcke, die nicht zusammen mit dem proc o lambda Schlüsselwörter).

0 Stimmen

Ich könnte zwar ein Beispiel hinzufügen, aber ich denke, mein Beispiel ist schon kompliziert genug.

0 Stimmen

Aus irgendeinem Grund verhalten sich in Blöcke umgewandelte Lambdas so, als wären sie immer Blöcke gewesen: [1].map &lambda{return "lambda"} von der Funktion zurück. Ist dies ein Fehler in JRuby?

8voto

Vitaly Kushner Punkte 8959

Betrachten Sie es einmal so: Proc.new erzeugt einfach einen Codeblock, der Teil der aufrufenden Funktion ist. proc/lambda erzeugen eine anonyme Funktion, die spezielle Bindungen hat. Ein paar Code-Beispiele werden Ihnen 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

ist gleichbedeutend mit

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

  return "return from foo" 
end

es ist also klar, dass die Rückgabe nur von der Funktion 'foo' kommt

im Gegensatz dazu:

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

entspricht (ohne Berücksichtigung der Bindungen, 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

Das heißt, es wird nicht von foo zurückkehren und stattdessen mit der nächsten Anweisung fortfahren.

5 Stimmen

Auch wenn dies eine alte Frage ist, beachten Sie bitte, dass es einen Unterschied zwischen Ruby 1.8.7 und 1.9.3 gibt, wo in letzterem Kernel.proc sich wie Proc.new statt wie Lambda verhält.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X