ruby-mine

exploring the mine

Ruby 1.9: Pflichtargumente nach optionalen Argumenten

von murphy am 10.01.2008 (15 Uhr)

Da Fragen aufgetaucht sind, beleuchten wir das neue Feature von Ruby 1.9 mal genauer. Nehmen wir uns eine Funktion, die alle möglichen Arten von Argumenten hat:

def foo(a, b = :default1, c = :default2, *d, e, &f)
  [a, b, c, d, e, f]
end

Die Regeln

Wichtig: Keines dieser Argumente hat etwas mit der offenen Hash-Notation (BluesBrother.find :all, :limit => 2) zu tun. Das ist einfach nur das letzte Argument, was übergeben wird. Wo es landet, bestimmt die Signatur.

Pflichtargumente müssen immer angegeben werden, und werden der Reihe nach – von links nach rechts – zugewiesen. foo hat 2 Pflichtargumente; wenn man die Methode mit weniger Argumenten aufruft, gibt es einen ArgumentError:

foo 1  # => ArgumentError

Übergibt man exakt die Mindestanzahl an Argumenten, erhalten alle optionalen Argumente ihren Standardwert, das Rest-Argument erhält [], und die Pflichtargumente werden von links nach rechts und (neu) von rechts nach links zugewiesen (je nachdem, ob sie vor oder nach den optionalen Argumenten stehen):

foo 1, 2                    # => [1, :default1, :default2, [], 2, nil]

Wenn mehr Argumente angegeben werden, als es Pflichtargumente gibt, werden die optionalen Argumente von links nach rechts zugewiesen; verbleibende optionale Argumente erhalten ihren Standardwert, und das Rest-Argument erhält wieder []:

foo 1, 2                    # => [1, :default1, :default2, [], 2, nil]
foo 1, 2, 3                 # => [1, 2, :default2, [], 3, nil]
foo 1, 2, 3, 4              # => [1, 2, 3, [], 4, nil]

e, das letzte Pflichtargument, erhält also immer den letzten Wert.

Sind dann immer noch Argumente übrig, landen sie der Reihe nach im Rest-Argument:

foo 1, 2, 3, 4, 5           # => [1, 2, 3, [4], 5, nil]
foo 1, 2, 3, 4, 5, 6        # => [1, 2, 3, [4, 5], 6, nil]

Ein Block-Argument ist immer ein optionales Argument (Standardwert: nil), das den Block enthält, wenn einer angegeben wurde. Es muss immer am Ende stehen, sowohl in der Methodensignatur als auch beim Aufruf. Übergeben kann man es entweder mit {}/do…end oder mit dem &-Präfix.

foo 1 {} rescue ArgumentError   # => ArgumentError
foo 1, 2                    {}  # => [1, :default1, :default2, [], 2, #<Proc:0x3d101c@-:15>]
foo 1, 2, 3                 {}  # => [1, 2, :default2, [], 3, #<Proc:0x3d0d74@-:16>]
foo 1, 2, 3, 4              {}  # => [1, 2, 3, [], 4, #<Proc:0x3d0ae0@-:17>]
foo 1, 2, 3, 4, 5           {}  # => [1, 2, 3, [4], 5, #<Proc:0x3d084c@-:18>]
foo 1, 2, 3, 4, 5, 6        {}  # => [1, 2, 3, [4, 5], 6, #<Proc:0x3d05a4@-:19>]

Einschränkungen


Kommentar schreiben

Name (notwendig)

Mail (wird nicht veröffentlicht)

Webseite


Kommentare

  1. paddor schrieb am 11.01.2008 (12 Uhr)

    Ich verstehe jetzt die Funktionalitaet, danke! Aber: Wozu ist das ganze gut? Laesst dies einem nicht zum unschoenen Programmieren neigen? Denn man koennte doch einfach immer alle Pflichtargumente am Anfang der Signatur definieren, oder ist das zu wenig "komfortabel"? Gibt's bestimmte Vorteile?

  2. WoNáDo schrieb am 11.01.2008 (18 Uhr)

    "Aber: Wozu ist das ganze gut?" - Auf Anhieb fällt mir dazu eine Ruby-Domäne ein: Embedded DSLs. Da gibt es bestimmt Situationen, bei denen der DSL-Designer die Parameter so angeordnet haben möchte, dass die Zuordnung der aktuellen Werte an die Parameter auf eine bestimmte Art erfolgt.