ruby-mine

exploring the mine

#lambda, #proc und #instance_eval

von skade am 07.09.2010 (10 Uhr)

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

  1. Kai schrieb am 07.09.2010 (15 Uhr)

    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.

  2. J-_-L schrieb am 07.09.2010 (17 Uhr)

    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..

  3. Kai schrieb am 07.09.2010 (17 Uhr)

    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.

  4. Skade schrieb am 07.09.2010 (19 Uhr)

    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.

  5. rbjl schrieb am 08.09.2010 (00 Uhr)

    Interessantes Argument. Weißt du zufällig spontan, ob das auch Performancegewinn bringt?

  6. murphy schrieb am 08.09.2010 (00 Uhr)

    Danke für die lehrreichen Erklärungen :D

  7. Kai schrieb am 08.09.2010 (02 Uhr)

    Also lambda war bei mir schon immer einen Tick performanter als Proc.new. Aber das war kaum signifikant.