2009-07-05 9 views
2

In Ruby 1.8 (meine Version ist 1.8.7-72), dieser Code:innen für Schleife in Lambda-Abstürzen in Ruby Rückkehr 1.8

foo = lambda do 
    for j in 1..2 
    return 
    end 
end 
foo.call 

stürzt mit einem LocalJumpError:

test2.rb:3: unexpected return (LocalJumpError) 
    from test2.rb:2:in `each' 
    from test2.rb:2 
    from test2.rb:6:in `call' 
    from test2.rb:6 

Warum tut es das? Allerdings scheint es auf meiner Version von Ruby 1.9 gut zu laufen.

Edit: es ist nicht nur die Rückkehr in einem Lambda; das Folgende läuft gut:

foo = lambda do 
    return 
end 
foo.call 

Antwort

8

Was passiert ist, dass die for-Anweisung in der Mitte des Lambda intern in einen Block umgewandelt wird. In Ruby sind Return-Anweisungen innerhalb von Blöcken auf ihre umschließende Methode beschränkt. Beachten Sie Folgendes:

def bar 
    foo = lambda do 
    for j in 1..2 
     return j 
    end 
    end 
    foo[] 
end 
p bar 

Wenn bar läuft, 1 zurückgegeben, weil die return auf die gesamte bar Methode scoped ist. Um von Blöcken zurückzukommen, möchten Sie next oder break verwenden, die beide Parameter übernehmen. Überlegen Sie:

def bar 
    foo = lambda do 
    for j in 1..2 
     break j 
    end 
    end 
    foo[] + 1 
end 
p bar 

Diese Pause gibt Sie vom Block zurück und blockiert alle nachfolgenden Iterationen. In diesem Fall würde der Aufruf bar2 zurückgeben, da der Iterator 1 zurückgibt und foo[] + 1 daher 2 zurückgibt.

Wenn das alles verwirrend klang, ist die Hauptsache zu erkennen, dass die Rückkehr innerhalb von Blöcken auf eine umgebende Methode beschränkt ist, und ohne eine umgebende Methode wird eine ausgelöst.

+0

Warum kann es nicht stattdessen zum Lambda Bereich? Ich kann nicht einfach aus der Schleife ausbrechen, denn in meinem ursprünglichen Szenario gibt es Code nach der for-Schleife, die ich überspringen muss – user102008

+0

Sie könnten versuchen, eine echte Methode, anstatt ein Lambda zu verwenden. –

+0

Verdammt. Ruby 1.8 erlaubt keine Rückkehr von innerhalb eines Procs innerhalb einer Methode, die über define_method definiert wurde. Vergiss das. –

0

können Sie die Schleife und den Rest des Lambda mit einem Wurf/catch

foo = lambda do 
     catch(:escape) do 
      for j in 1..2 
      throw :escape 
      end 
      # other code that won't get run 
     end # catch(:escape) 
     end # lambda 

throw nimmt auch einen optionalen zweiten Paramter, entgehen, die Sie einen Wert zurückgeben können.

+0

Dies ist eine ziemlich schwergewichtige Lösung, um eine Rendite zu emulieren, denke ich. –

+0

gibt es nicht viele andere Optionen, _emulating_ eine Rückkehr (die anderen Optionen sind Ausnahmen und callcc). Ich vermute, dass es das leichteste Gewicht ist. Die beste Lösung ist wahrscheinlich nur eine Methode, aber das kann unangenehm sein, wenn der Bereich, den Sie schließen müssen, nicht gut dafür geeignet ist. Deshalb ist 1.9, dass Sie von Lambda's zurückkehren, nett. –

+0

Ich habe in Ruby 1.8 eine Möglichkeit hinzugefügt, Ruby 1.9 lambdas in meiner Antwort zu emulieren, was leichter wäre als throw/catch. –