Methodenaufrufe
Das Aufrufen einer Methode sendet eine Nachricht an ein Objekt, damit dieses eine bestimmte Aufgabe ausführen kann.
In Ruby senden Sie eine Nachricht an ein Objekt wie folgt:
my_method()
Beachten Sie, dass die Klammern optional sind.
my_method
Außer wenn es einen Unterschied zwischen der Verwendung und dem Weglassen von Klammern gibt, verwendet dieses Dokument Klammern, wenn Argumente vorhanden sind, um Verwirrung zu vermeiden.
Dieser Abschnitt behandelt nur das Aufrufen von Methoden. Siehe auch die Syntaxdokumentation zum Definieren von Methoden.
Empfänger
self ist der Standardempfänger. Wenn Sie keinen Empfänger angeben, wird self verwendet. Um einen Empfänger anzugeben, verwenden Sie ..
my_object.my_method
Dies sendet die Nachricht my_method an my_object. Jedes Objekt kann ein Empfänger sein, aber abhängig von der Sichtbarkeit der Methode kann das Senden einer Nachricht einen NoMethodError auslösen.
Sie können auch :: verwenden, um einen Empfänger zu bezeichnen, dies wird jedoch aufgrund der Verwechslungsgefahr mit :: für Namespaces selten verwendet.
Methodenaufrufe verketten
Sie können Methodenaufrufe „verkettet“, indem Sie einen Methodenaufruf sofort mit einem weiteren fortsetzen.
Dieses Beispiel verkettet die Methoden Array#append und Array#compact.
a = [:foo, 'bar', 2] a1 = [:baz, nil, :bam, nil] a2 = a.append(*a1).compact a2 # => [:foo, "bar", 2, :baz, :bam]
Details
-
Die erste Methode
mergeerstellt eine Kopie vona, fügt jedes Element vona1(separat) an die Kopie an und gibt diese zurück.[:foo, "bar", 2, :baz, nil, :bam, nil]
-
Die verkettete Methode
compacterstellt eine Kopie dieses Rückgabewerts, entfernt dessennil-Werte und gibt diese zurück.[:foo, "bar", 2, :baz, :bam]
Sie können Methoden verketten, die sich in verschiedenen Klassen befinden. Dieses Beispiel verkettet die Methoden Hash#to_a und Array#reverse.
h = {foo: 0, bar: 1, baz: 2} h.to_a.reverse # => [[:baz, 2], [:bar, 1], [:foo, 0]]
Details
-
Die erste Methode
Hash#to_akonvertiertain ein Array und gibt dieses zurück.[[:foo, 0], [:bar, 1], [:baz, 2]]
-
Die verkettete Methode
Array#reverseerstellt eine Kopie dieses Rückgabewerts, kehrt dessen Reihenfolge um und gibt diese zurück.[[:baz, 2], [:bar, 1], [:foo, 0]]
Sicherer Navigationsoperator
&., der „sichere Navigationsoperator“ genannt wird, ermöglicht das Überspringen eines Methodenaufrufs, wenn der Empfänger nil ist. Er gibt nil zurück und wertet die Argumente der Methode nicht aus, wenn der Aufruf übersprungen wird.
REGEX = /(ruby) is (\w+)/i "Ruby is awesome!".match(REGEX).values_at(1, 2) # => ["Ruby", "awesome"] "Python is fascinating!".match(REGEX).values_at(1, 2) # NoMethodError: undefined method `values_at' for nil:NilClass "Python is fascinating!".match(REGEX)&.values_at(1, 2) # => nil
Dies ermöglicht die einfache Verkettung von Methoden, die leere Werte zurückgeben könnten. Beachten Sie, dass &. nur den nächsten Aufruf überspringt. Für eine längere Kette ist es notwendig, den Operator auf jeder Ebene hinzuzufügen.
"Python is fascinating!".match(REGEX)&.values_at(1, 2).join(' - ') # NoMethodError: undefined method `join' for nil:NilClass "Python is fascinating!".match(REGEX)&.values_at(1, 2)&.join(' - ') # => nil
Argumente
Beim Senden einer Nachricht gibt es drei Arten von Argumenten: Positionsargumente, Schlüsselwortargumente (oder benannte Argumente) und das Blockargument. Jede gesendete Nachricht kann eine, zwei oder alle Arten von Argumenten verwenden, aber die Argumente müssen in dieser Reihenfolge angegeben werden.
Alle Argumente in Ruby werden per Referenz übergeben und nicht verzögert ausgewertet.
Jedes Argument wird durch ein , getrennt.
my_method(1, '2', :three)
Argumente können ein Ausdruck, ein Hash-Argument sein
'key' => value
oder ein Schlüsselwortargument.
key: value
Hash und Schlüsselwortargumente müssen zusammenhängend sein und nach allen Positionsargumenten stehen, können aber gemischt werden.
my_method('a' => 1, b: 2, 'c' => 3)
Positionsargumente
Die Positionsargumente für die Nachricht folgen dem Methodennamen.
my_method(argument1, argument2)
In vielen Fällen sind Klammern beim Senden einer Nachricht nicht notwendig.
my_method argument1, argument2
Klammern sind jedoch erforderlich, um Mehrdeutigkeit zu vermeiden. Dies löst einen SyntaxError aus, da Ruby nicht weiß, an welche Methode argument3 gesendet werden soll.
method_one argument1, method_two argument2, argument3
Wenn die Methodendefinition ein *argument hat, werden zusätzliche Positionsargumente in argument als Array in der Methode zugewiesen.
Wenn die Methodendefinition keine Schlüsselwortargumente enthält, werden die Schlüsselwort- oder Hash-ähnlichen Argumente als einzelner Hash dem letzten Argument zugewiesen.
def my_method(options) p options end my_method('a' => 1, b: 2) # prints: {'a'=>1, :b=>2}
Wenn zu viele Positionsargumente angegeben werden, wird ein ArgumentError ausgelöst.
Standard-Positionsargumente
Wenn die Methode Standardargumente definiert, müssen Sie nicht alle Argumente an die Methode übergeben. Ruby füllt die fehlenden Argumente der Reihe nach auf.
Zuerst behandeln wir den einfachen Fall, bei dem die Standardargumente auf der rechten Seite stehen. Betrachten Sie diese Methode:
def my_method(a, b, c = 3, d = 4) p [a, b, c, d] end
Hier haben c und d Standardwerte, die Ruby für Sie anwendet. Wenn Sie nur zwei Argumente an diese Methode senden:
my_method(1, 2)
Sie sehen, wie Ruby [1, 2, 3, 4] ausgibt.
Wenn Sie drei Argumente senden:
my_method(1, 2, 5)
Sie sehen, wie Ruby [1, 2, 5, 4] ausgibt.
Ruby füllt die fehlenden Argumente von links nach rechts auf.
Ruby erlaubt Standardwerte in der Mitte von Positionsargumenten. Betrachten Sie diese kompliziertere Methode:
def my_method(a, b = 2, c = 3, d) p [a, b, c, d] end
Hier haben b und c Standardwerte. Wenn Sie nur zwei Argumente an diese Methode senden:
my_method(1, 4)
Sie sehen, wie Ruby [1, 2, 3, 4] ausgibt.
Wenn Sie drei Argumente senden:
my_method(1, 5, 6)
Sie sehen, wie Ruby [1, 5, 3, 6] ausgibt.
Dies in Worte zu fassen, wird kompliziert und verwirrend. Ich werde es stattdessen in Variablen und Werten beschreiben.
Zuerst wird 1 an a zugewiesen, dann wird 6 an d zugewiesen. Dadurch bleiben nur die Argumente mit Standardwerten übrig. Da 5 noch keinem Wert zugewiesen wurde, wird es b gegeben und c verwendet seinen Standardwert von 3.
Schlüsselwortargumente
Schlüsselwortargumente folgen allen Positionsargumenten und werden wie Positionsargumente durch Kommas getrennt.
my_method(positional1, keyword1: value1, keyword2: value2)
Alle nicht angegebenen Schlüsselwortargumente verwenden den Standardwert aus der Methodendefinition. Wenn ein Schlüsselwortargument angegeben wird, das die Methode nicht aufgelistet hat, und die Methodendefinition keine beliebigen Schlüsselwortargumente akzeptiert, wird ein ArgumentError ausgelöst.
Der Wert eines Schlüsselwortarguments kann weggelassen werden, was bedeutet, dass der Wert aus dem Kontext anhand des Namens des Schlüssels bezogen wird.
keyword1 = 'some value' my_method(positional1, keyword1:) # ...is the same as my_method(positional1, keyword1: keyword1)
Seien Sie sich bewusst, dass die Parsing-Reihenfolge unerwartet sein kann, wenn auch die Methodenkopfklammern weggelassen werden.
my_method positional1, keyword1: some_other_expression # ...is actually parsed as my_method(positional1, keyword1: some_other_expression)
Blockargument
Das Blockargument sendet eine Closure aus dem aufrufenden Gültigkeitsbereich an die Methode.
Das Blockargument steht beim Senden einer Nachricht an eine Methode immer zuletzt. Ein Block wird mit do ... end oder { ... } an eine Methode gesendet.
my_method do # ... end
oder
my_method { # ... }
do end hat eine niedrigere Priorität als { }, also
method_1 method_2 { # ... }
sendet den Block an method_2, während
method_1 method_2 do # ... end
sendet den Block an method_1. Beachten Sie, dass in beiden Fällen, wenn Klammern verwendet werden, der Block an method_1 gesendet wird.
Ein Block kann Argumente von der Methode akzeptieren, an die er gesendet wurde. Argumente werden ähnlich wie eine Methode Argumente definiert. Die Argumente des Blocks stehen in | ... | nach dem öffnenden do oder {.
my_method do |argument1, argument2| # ... end
Block-lokale Argumente
Sie können auch Block-lokale Argumente für einen Block deklarieren, indem Sie ; in der Liste der Blockargumente verwenden. Die Zuweisung an ein Block-lokales Argument überschreibt keine lokalen Argumente außerhalb des Blocks im Gültigkeitsbereich des Aufrufers.
def my_method yield self end place = "world" my_method do |obj; place| place = "block" puts "hello #{obj} this is #{place}" end puts "place is: #{place}"
Dies gibt aus:
hello main this is block place is: world
Die Variable place im Block ist also nicht dieselbe Variable place wie außerhalb des Blocks. Wenn Sie ; place aus den Blockargumenten entfernen, erhalten Sie dieses Ergebnis:
hello main this is block place is: block
Positionsargumente entpacken
Angenommen, die folgende Methode:
def my_method(argument1, argument2, argument3) end
Sie können ein Array mit dem *-Operator (oder Splat-Operator) in eine Argumentliste umwandeln.
arguments = [1, 2, 3] my_method(*arguments)
oder
arguments = [2, 3] my_method(1, *arguments)
Beides ist äquivalent zu:
my_method(1, 2, 3)
Der *-Entpackungsoperator kann auf jedes Objekt angewendet werden, nicht nur auf Arrays. Wenn das Objekt eine to_a-Methode unterstützt, wird diese Methode aufgerufen und sollte ein Array zurückgeben. Die Elemente dieses Arrays werden als separate Positionsargumente übergeben.
class Name def initialize(name) @name = name end def to_a = @name.split(' ') end name = Name.new('Jane Doe') p(*name) # prints separate values: # Jane # Doe
Wenn das Objekt keine to_a-Methode hat, wird das Objekt selbst als ein Argument übergeben.
class Name def initialize(name) @name = name end end name = Name.new('Jane Doe') p(*name) # Prints the object itself: # #<Name:0x00007f9d07bca650 @name="Jane Doe">
Dies ermöglicht die polymorphe Behandlung eines oder mehrerer Argumente. Beachten Sie auch, dass *nil zu einer leeren Liste von Argumenten entpackt wird, so dass bedingtes Entpacken möglich ist.
my_method(*(some_arguments if some_condition?))
Wenn die to_a-Methode existiert und kein Array zurückgibt, führt dies beim Entpacken zu einem Fehler.
class Name def initialize(name) @name = name end def to_a = @name end name = Name.new('Jane Doe') p(*name) # can't convert Name to Array (Name#to_a gives String) (TypeError)
Sie können auch ** (wie nachfolgend beschrieben) verwenden, um ein Hash in Schlüsselwortargumente umzuwandeln.
Wenn die Anzahl der Objekte im Array nicht mit der Anzahl der Argumente für die Methode übereinstimmt, wird ein ArgumentError ausgelöst.
Wenn der Splat-Operator zuerst im Aufruf steht, müssen Klammern verwendet werden, um eine Mehrdeutigkeit der Interpretation als Entpackungsoperator oder Multiplikationsoperator zu vermeiden. In diesem Fall gibt Ruby in einem ausführlichen Modus eine Warnung aus.
my_method *arguments # warning: '*' interpreted as argument prefix my_method(*arguments) # no warning
Schlüsselwortargumente entpacken
Angenommen, die folgende Methode:
def my_method(first: 1, second: 2, third: 3) end
Sie können ein Hash mit dem **-Operator (Keyword Splat) in Schlüsselwortargumente umwandeln.
arguments = { first: 3, second: 4, third: 5 } my_method(**arguments)
oder
arguments = { first: 3, second: 4 } my_method(third: 5, **arguments)
Beides ist äquivalent zu:
my_method(first: 3, second: 4, third: 5)
Der **-Entpackungsoperator kann auf jedes Objekt angewendet werden, nicht nur auf Hashes. Wenn das Objekt eine to_hash-Methode unterstützt, wird diese Methode aufgerufen und sollte ein Hash zurückgeben. Die Elemente dieses Hashs werden als Schlüsselwortargumente übergeben.
class Name def initialize(name) @name = name end def to_hash = {first: @name.split(' ').first, last: @name.split(' ').last} end name = Name.new('Jane Doe') p(**name) # Prints: {name: "Jane", last: "Doe"}
Im Gegensatz zum *-Operator löst ** einen Fehler aus, wenn es auf einem Objekt verwendet wird, das nicht auf to_hash reagiert. Die einzige Ausnahme ist nil, das diese Methode nicht explizit definiert, aber dennoch für die **-Entpackung verwendet werden darf, ohne zusätzliche Schlüsselwortargumente hinzuzufügen.
Auch dies ermöglicht bedingtes Entpacken.
my_method(some: params, **(some_extra_params if pass_extra_params?))
Wie der *-Operator löst ** einen Fehler aus, wenn das Objekt auf to_hash reagiert, aber kein Hash zurückgibt.
Wenn die Methodendefinition den Keyword-Splat-Operator verwendet, um beliebige Schlüsselwortargumente zu sammeln, werden diese nicht von * gesammelt.
def my_method(*a, **kw) p arguments: a, keywords: kw end my_method(1, 2, '3' => 4, five: 6)
Gibt aus:
{:arguments=>[1, 2], :keywords=>{'3'=>4, :five=>6}}
Proc zu Block-Konvertierung
Gegeben sei eine Methode, die einen Block verwendet:
def my_method yield self end
Sie können einen Proc oder ein Lambda mit dem &-Operator (Blockkonvertierung) in ein Blockargument umwandeln.
argument = proc { |a| puts "#{a.inspect} was yielded" } my_method(&argument)
Wenn der Blockkonvertierungsoperator zuerst im Aufruf steht, müssen Klammern verwendet werden, um eine Warnung zu vermeiden.
my_method &argument # warning my_method(&argument) # no warning
Methoden-Lookup
Wenn Sie eine Nachricht senden, sucht Ruby nach der Methode, die dem Namen der Nachricht für den Empfänger entspricht. Methoden werden in Klassen und Modulen gespeichert, sodass der Methoden-Lookup diese durchläuft, nicht die Objekte selbst.
Hier ist die Reihenfolge des Methoden-Lookups für die Klasse oder das Modul R des Empfängers:
-
Die vorangestellten Module von
Rin umgekehrter Reihenfolge. -
Für eine übereinstimmende Methode in
R. -
Die inkludierten Module von
Rin umgekehrter Reihenfolge.
Wenn R eine Klasse mit einer Oberklasse ist, wird dies mit der Oberklasse von R wiederholt, bis eine Methode gefunden wird.
Sobald eine Übereinstimmung gefunden wurde, stoppt der Methoden-Lookup.
Wenn keine Übereinstimmung gefunden wird, wiederholt sich dies von vorne, sucht aber nach method_missing. Die Standard-method_missing ist BasicObject#method_missing, die einen NameError auslöst, wenn sie aufgerufen wird.
Wenn Verfeinerungen (ein experimentelles Feature) aktiv sind, ändert sich der Methoden-Lookup. Weitere Details finden Sie in der Dokumentation zu Verfeinerungen.