Lambdas unterscheiden sich in Ruby 1.9 nun stärker von Procs: sie akzeptieren nur noch genau ihre Argumentliste. Als Illustration:
1 2 3 4 |
a = proc {|a| puts a }
a.call #=> nil
b = lambda {|b| puts a }
b.call #=> ArgumentError: wrong number of arguments (0 for 1) |
Procs sind als laxer: die Parameterliste und die Argumentliste müssen nicht unbedingt gleich lang sein. Nicht übergebene Parameter werden zu nil, zuviel übergebene einfach ignoriert. Proc-Objekte verhalten sich also wie inline-Blöcke, Lambdas wie Methoden. Ein Beispiel, in dem das üblich ist, ist instance_eval. instance_eval übergibt dem Block das Object selbst. Als Beispiel:
Object.instance_eval { |o| o.object_id == Object.object_id } #=> true |
Nun kann es passieren, dass der Block an anderer Stelle erstellt wurde und erst später an instance_eval übergeben wird. Da häufig in so einem Fall das Zielobjekt ausser acht gelassen will, sollte man tunlichst aufpassen, keine lambdas mehr zu verwenden. Sonst wird man von einer wenig aussagekräftigen Fehlermeldung begrüßt:
1 2 3 |
fun = lambda { new }
Object.instance_eval &fun
#=> ArgumentError: wrong number of arguments (0 for 1) |
Korrekt sind für diesen Einsatz nur noch Procs.
Ein letztes Wort zur Warnung: Ruby 1.9.1 verhält sich an genau dieser Stelle buggy und übergibt, im Gegensatz zu Ruby 1.8.x und 1.9.2, das Objekt nicht. Das kann dazu führen, dass Code auf 1.8.x und 1.9.1 funktioniert, aber beim Umstieg auf 1.9.2 nicht mehr.
Kommentare
Wobei auch in früheren Versionen lambda schon strikter war. Allerdings gab es wohl eine Ausnahmeregelung: Wenn lambda 0 oder 1 Parameter annahm, dann wurden die übergebenen Parameter entweder gar nicht, oder als Array übergeben. Bei mehr als einem Parameter wurden aber schon ArgumentErrors ausgegeben, wenn lambda nicht die korrekte Zahl an Parametern bekam. Auch wenn diese Ausnahmeregelung für dein Beispiel mit instance_eval ganz praktisch war, finde ich die jetzige Regelung doch sinnvoller. lambdas dürften sich jetzt genau wie Methoden verhalten.
Btw, verwendet eigentlich jemand von euch den stabby-syntax?
dobule =->(n) {n2} vs double = lambda{|n| n2}
Ich finde, obwohl er schon schick aussieht, find ich den normalen Syntax meist doch hübscher..
In den meisten Fällen übergibt man den Block ja direkt. Die neue Syntax wurde afair vor allem deshalb eingeführt, damit man einem Block Blockparameter, sowie optionale Parameter übergeben kann.
Alle Argumentlisten in Ruby sind mit Ruby 1.9 gleich.
Ich verwende Stabby trotzdem, mir gefällt sie und für mich selbst programmier ich meist eh nur mir 1.9. Die Begründung dafür ist eine andere: genau wie bei {} und [] (Hash und Array) weiss schon der Parser, welches Objekt dort landet. Kernel#lambda und #proc dahingegen können überschrieben werden und müssen daher ausgeführt werden.
Interessantes Argument. Weißt du zufällig spontan, ob das auch Performancegewinn bringt?
Danke für die lehrreichen Erklärungen :D
Also lambda war bei mir schon immer einen Tick performanter als Proc.new. Aber das war kaum signifikant.