Methoden

Methoden implementieren die Funktionalität Ihres Programms. Hier ist eine einfache Methodendefinition

def one_plus_one
  1 + 1
end

Eine Methodendefinition besteht aus dem Schlüsselwort def, einem Methodennamen, dem Körper der Methode, dem return-Wert und dem Schlüsselwort end. Wenn die Methode aufgerufen wird, wird der Körper der Methode ausgeführt. Diese Methode gibt 2 zurück.

Seit Ruby 3.0 gibt es auch eine Kurzschreibweise für Methoden, die aus genau einem Ausdruck bestehen

def one_plus_one = 1 + 1

Dieser Abschnitt behandelt nur die Definition von Methoden. Siehe auch die Syntaxdokumentation zum Aufrufen von Methoden.

Methodennamen

Methodennamen können eine der Operatoren sein oder müssen mit einem Buchstaben oder einem Zeichen mit gesetztem achten Bit beginnen. Sie dürfen Buchstaben, Zahlen, einen _ (Unterstrich) oder ein Zeichen mit gesetztem achten Bit enthalten. Die Konvention ist, Unterstriche zur Trennung von Wörtern in einem Methodennamen mit mehreren Wörtern zu verwenden

def method_name
  puts "use underscores to separate words"
end

Ruby-Programme müssen in einem US-ASCII-kompatiblen Zeichensatz wie UTF-8, ISO-8859-1 usw. geschrieben sein. In solchen Zeichensätzen zeigt ein gesetztes achtes Bit ein erweitertes Zeichen an. Ruby erlaubt Methodennamen und andere Bezeichner, solche Zeichen zu enthalten. Ruby-Programme dürfen einige Zeichen wie ASCII NUL (\x00) nicht enthalten.

Die folgenden sind Beispiele für gültige Ruby-Methoden

def hello
  "hello"
end

def こんにちは
  puts "means hello in Japanese"
end

Typischerweise sind Methodennamen US-ASCII-kompatibel, da die Tasten zu ihrer Eingabe auf allen Tastaturen vorhanden sind.

Methodennamen dürfen mit einem ! (Bang oder Ausrufezeichen), einem ? (Fragezeichen) oder einem = (Gleichheitszeichen) enden.

Bang-Methoden (! am Ende des Methodennamens) werden wie jede andere Methode aufgerufen und ausgeführt. Nach Konvention gilt eine Methode mit einem Ausrufezeichen oder Bang jedoch als gefährlich. In der Kernbibliothek von Ruby bedeutet eine gefährliche Methode, dass eine Methode, die mit einem Bang (!) endet, anzeigt, dass sie im Gegensatz zu ihrem Nicht-Bang-Äquivalent ihren Empfänger dauerhaft modifiziert. Fast immer hat die Ruby-Kernbibliothek ein Nicht-Bang-Gegenstück (Methodenname, der NICHT mit ! endet) zu jeder Bang-Methode (Methodenname, der mit ! endet), die den Empfänger nicht modifiziert. Diese Konvention gilt typischerweise für die Ruby-Kernbibliothek, gilt aber möglicherweise nicht für andere Ruby-Bibliotheken.

Methoden, die mit einem Fragezeichen enden, geben nach Konvention Boole'sche Werte zurück, aber sie müssen nicht immer nur true oder false zurückgeben. Oft geben sie ein Objekt zurück, um einen wahren Wert (oder einen "wahrheitsfähigen" Wert) anzuzeigen.

Methoden, die mit einem Gleichheitszeichen enden, zeigen eine Zuweisungsmethode an.

class C
  def attr
    @attr
  end

  def attr=(val)
    @attr = val
  end
end

c = C.new
c.attr      #=> nil
c.attr = 10 # calls "attr=(10)"
c.attr      #=> 10

Zuweisungsmethoden können nicht mit der Kurzschreibweise definiert werden.

Dies sind Methodennamen für die verschiedenen Ruby-Operatoren. Jeder dieser Operatoren akzeptiert nur ein Argument. Nach dem Operator folgt die typische Verwendung oder der Name des Operators. Die Schaffung einer alternativen Bedeutung für den Operator kann zu Verwirrung führen, da der Benutzer erwartet, dass Plus Dinge addiert, Minus Dinge subtrahiert usw. Außerdem können Sie die Priorität der Operatoren nicht ändern.

+

add

-

subtrahieren

*

multiplizieren

**

Potenz

/

dividieren

%

Modulodivision, String#%

&

UND

|

ODER

^

XOR (exklusives ODER)

>>

Rechtsverschiebung

<<

Linksverschiebung, anhängen

==

gleich

!=

ungleich

===

Fallgleichheit. Siehe Object#===

=~

Musterabgleich. (Nicht nur für reguläre Ausdrücke)

!~

stimmt nicht überein

<=>

Vergleich aka Spaceship-Operator. Siehe Comparable

<

kleiner als

<=

kleiner als oder gleich

>

größer als

>=

größer als oder gleich

Um unäre Methoden Minus und Plus zu definieren, folgen Sie dem Operator mit einem @ wie in +@

class C
  def -@
    puts "you inverted this object"
  end
end

obj = C.new

-obj # prints "you inverted this object"

Das @ wird benötigt, um unäre Minus- und Plus-Operatoren von binären Minus- und Plus-Operatoren zu unterscheiden.

Sie können auch Tilde- und Nicht- (!) unäre Methoden mit @ versehen, dies ist jedoch nicht erforderlich, da es keine binären Tilde- und Nicht-Operatoren gibt.

Unäre Methoden akzeptieren keine Argumente.

Zusätzlich können Methoden für Elementreferenz und Zuweisung definiert werden: [] bzw. []=. Beide können ein oder mehrere Argumente annehmen, und die Elementreferenz kann keines annehmen.

class C
  def [](a, b)
    puts a + b
  end

  def []=(a, b, c)
    puts a * b + c
  end
end

obj = C.new

obj[2, 3]     # prints "5"
obj[2, 3] = 4 # prints "10"

Rückgabewerte

Standardmäßig gibt eine Methode den letzten ausgewerteten Ausdruck im Körper der Methode zurück. Im obigen Beispiel war der letzte (und einzige) ausgewertete Ausdruck die einfache Summe 1 + 1. Das Schlüsselwort return kann verwendet werden, um explizit zu machen, dass eine Methode einen Wert zurückgibt.

def one_plus_one
  return 1 + 1
end

Es kann auch verwendet werden, um eine Methode zurückzugeben, bevor der letzte Ausdruck ausgewertet wird.

def two_plus_two
  return 2 + 2
  1 + 1  # this expression is never evaluated
end

Beachten Sie, dass bei Zuweisungsmethoden der Rückgabewert bei Verwendung der Zuweisungssyntax ignoriert wird. Stattdessen wird das Argument zurückgegeben

def a=(value)
  return 1 + value
end

p(self.a = 5) # prints 5

Der tatsächliche Rückgabewert wird bei direktem Aufruf der Methode zurückgegeben

p send(:a=, 5) # prints 6

Scope

Die Standard-Syntax zur Definition einer Methode

def my_method
  # ...
end

fügt die Methode einer Klasse hinzu. Sie können eine Instanzmethode für eine bestimmte Klasse mit dem Schlüsselwort class definieren

class C
  def my_method
    # ...
  end
end

Eine Methode kann auf einem anderen Objekt definiert werden. Sie können eine "Klassenmethode" (eine Methode, die auf der Klasse und nicht auf einer Instanz der Klasse definiert ist) wie folgt definieren

class C
  def self.my_method
    # ...
  end
end

Dies ist jedoch lediglich ein Sonderfall einer größeren syntaktischen Leistung in Ruby, der Fähigkeit, Methoden zu jedem Objekt hinzuzufügen. Klassen sind Objekte, daher ist das Hinzufügen von Klassenmethoden einfach das Hinzufügen von Methoden zum Klassenobjekt.

Die Syntax zum Hinzufügen einer Methode zu einem Objekt lautet wie folgt

greeting = "Hello"

def greeting.broaden
  self + ", world!"
end

greeting.broaden # returns "Hello, world!"

self ist ein Schlüsselwort, das sich auf das aktuelle Objekt bezieht, das vom Compiler betrachtet wird, was die Verwendung von self bei der Definition einer Klassenmethode oben etwas klarer machen kann. Tatsächlich kann das Beispiel des Hinzufügens einer hello-Methode zur Klasse String wie folgt umgeschrieben werden

def String.hello
  "Hello, world!"
end

Eine auf diese Weise definierte Methode wird als "Singleton-Methode" bezeichnet. broaden existiert nur für die String-Instanz greeting. Andere Strings werden broaden nicht haben.

Überschreiben

Wenn Ruby auf das Schlüsselwort def stößt, betrachtet es dies nicht als Fehler, wenn die Methode bereits existiert: es definiert sie einfach neu. Dies wird als *Überschreiben* bezeichnet. Ähnlich wie das Erweitern von Kernklassen ist dies eine potenziell gefährliche Fähigkeit und sollte sparsam eingesetzt werden, da sie zu unerwarteten Ergebnissen führen kann. Betrachten Sie zum Beispiel diese irb-Sitzung

>> "43".to_i
=> 43
>> class String
>>   def to_i
>>     42
>>   end
>> end
=> nil
>> "43".to_i
=> 42

Dies wird effektiv jeglichen Code sabotieren, der die Methode String#to_i zum Parsen von Zahlen aus Strings verwendet.

Argumente

Eine Methode kann Argumente akzeptieren. Die Argumentliste folgt dem Methodennamen

def add_one(value)
  value + 1
end

Beim Aufruf muss der Benutzer der add_one-Methode ein Argument bereitstellen. Das Argument ist eine lokale Variable im Methodenkörper. Die Methode addiert dann eins zu diesem Argument und gibt den Wert zurück. Wenn 1 übergeben wird, gibt diese Methode 2 zurück.

Die Klammern um die Argumente sind optional

def add_one value
  value + 1
end

Die Klammern sind bei Kurzschreibweisen für Methoden obligatorisch

# OK
def add_one(value) = value + 1
# SyntaxError
def add_one value = value + 1

Mehrere Argumente werden durch ein Komma getrennt

def add_values(a, b)
  a + b
end

Beim Aufruf müssen die Argumente in der exakten Reihenfolge bereitgestellt werden. Mit anderen Worten, die Argumente sind positional.

Standardwerte

Argumente können Standardwerte haben

def add_values(a, b = 1)
  a + b
end

Der Standardwert muss nicht zuerst erscheinen, aber Argumente mit Standardwerten müssen zusammen gruppiert werden. Das ist in Ordnung

def add_values(a = 1, b = 2, c)
  a + b + c
end

Dies löst einen SyntaxError aus

def add_values(a = 1, b, c = 1)
  a + b + c
end

Standardargumentwerte können sich auf Argumente beziehen, die bereits als lokale Variablen ausgewertet wurden, und Argumentwerte werden immer von links nach rechts ausgewertet. Daher ist dies zulässig

def add_values(a = 1, b = a)
  a + b
end
add_values
# => 2

Dies löst jedoch einen NameError aus (es sei denn, es gibt eine Methode namens b, die definiert ist)

def add_values(a = b, b = 1)
  a + b
end
add_values
# NameError (undefined local variable or method `b' for main:Object)

Array-Dekompression

Sie können ein Array dekomprimieren (entpacken oder Werte extrahieren) mithilfe zusätzlicher Klammern in den Argumenten

def my_method((a, b))
  p a: a, b: b
end

my_method([1, 2])

Dies gibt aus:

{:a=>1, :b=>2}

Wenn das Argument zusätzliche Elemente im Array hat, werden diese ignoriert

def my_method((a, b))
  p a: a, b: b
end

my_method([1, 2, 3])

Dies hat die gleiche Ausgabe wie oben.

Sie können einen * verwenden, um die restlichen Argumente zu sammeln. Dies teilt ein Array in ein erstes Element und den Rest auf

def my_method((a, *b))
  p a: a, b: b
end

my_method([1, 2, 3])

Dies gibt aus:

{:a=>1, :b=>[2, 3]}

Das Argument wird dekomprimiert, wenn es auf to_ary antwortet. Sie sollten to_ary nur definieren, wenn Sie Ihr Objekt anstelle eines Array verwenden können.

Die Verwendung der inneren Klammern verwendet nur eines der übergebenen Argumente. Wenn das Argument kein Array ist, wird es dem ersten Argument in der Dekompression zugewiesen und die restlichen Argumente in der Dekompression sind nil

def my_method(a, (b, c), d)
  p a: a, b: b, c: c, d: d
end

my_method(1, 2, 3)

Dies gibt aus:

{:a=>1, :b=>2, :c=>nil, :d=>3}

Sie können die Dekompression beliebig verschachteln

def my_method(((a, b), c))
  # ...
end

Array/Hash-Argument

Das Präfix eines Arguments mit * bewirkt, dass alle verbleibenden Argumente in ein Array konvertiert werden

def gather_arguments(*arguments)
  p arguments
end

gather_arguments 1, 2, 3 # prints [1, 2, 3]

Das Array-Argument muss vor allen Schlüsselwortargumenten erscheinen.

Es ist möglich, Argumente am Anfang oder in der Mitte zu sammeln

def gather_arguments(first_arg, *middle_arguments, last_arg)
  p middle_arguments
end

gather_arguments 1, 2, 3, 4 # prints [2, 3]

Das Array-Argument erfasst einen Hash als letzten Eintrag, wenn Schlüsselwörter vom Aufrufer nach allen Positionsargumenten bereitgestellt wurden.

def gather_arguments(*arguments)
  p arguments
end

gather_arguments 1, a: 2 # prints [1, {:a=>2}]

Dies geschieht jedoch nur, wenn die Methode keine Schlüsselwortargumente deklariert.

def gather_arguments_keyword(*positional, keyword: nil)
 p positional: positional, keyword: keyword
end

gather_arguments_keyword 1, 2, three: 3
#=> raises: unknown keyword: three (ArgumentError)

Beachten Sie auch, dass ein nacktes * verwendet werden kann, um Argumente zu ignorieren

def ignore_arguments(*)
end

Sie können auch ein nacktes * beim Aufrufen einer Methode verwenden, um die Argumente direkt an eine andere Methode weiterzugeben

def delegate_arguments(*)
  other_method(*)
end

Schlüsselwortargumente

Schlüsselwortargumente sind ähnlich wie Positionsargumente mit Standardwerten

def add_values(first: 1, second: 2)
  first + second
end

Beliebige Schlüsselwortargumente werden mit ** akzeptiert

def gather_arguments(first: nil, **rest)
  p first, rest
end

gather_arguments first: 1, second: 2, third: 3
# prints 1 then {:second=>2, :third=>3}

Beim Aufrufen einer Methode mit Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge erscheinen. Wenn vom Aufrufer ein unbekanntes Schlüsselwortargument gesendet wird und die Methode keine beliebigen Schlüsselwortargumente akzeptiert, wird ein ArgumentError ausgelöst.

Um ein bestimmtes Schlüsselwortargument zu erzwingen, fügen Sie dem Schlüsselwortargument keinen Standardwert hinzu

def add_values(first:, second:)
  first + second
end
add_values
# ArgumentError (missing keywords: first, second)
add_values(first: 1, second: 2)
# => 3

Beim Mischen von Schlüsselwortargumenten und Positionsargumenten müssen alle Positionsargumente vor allen Schlüsselwortargumenten erscheinen.

Beachten Sie auch, dass ** verwendet werden kann, um Schlüsselwortargumente zu ignorieren

def ignore_keywords(**)
end

Sie können ** auch beim Aufrufen einer Methode verwenden, um Schlüsselwortargumente an eine andere Methode weiterzuleiten

def delegate_keywords(**)
  other_method(**)
end

Um eine Methode als Schlüsselwörter akzeptierend zu markieren, aber keine Schlüsselwörter zu akzeptieren, können Sie **nil verwenden

def no_keywords(**nil)
end

Das Aufrufen einer solchen Methode mit Schlüsselwörtern oder einem nicht leeren Schlüsselwort-Splat führt zu einem ArgumentError. Diese Syntax wird unterstützt, damit Schlüsselwörter später zur Methode hinzugefügt werden können, ohne die Abwärtskompatibilität zu beeinträchtigen.

Wenn eine Methodendefinition keine Schlüsselwörter akzeptiert und die **nil-Syntax nicht verwendet wird, werden alle beim Aufruf der Methode bereitgestellten Schlüsselwörter in ein Positionsargument des Typs Hash konvertiert

def meth(arg)
  arg
end
meth(a: 1)
# => {:a=>1}

Block-Argument

Das Block-Argument wird durch & gekennzeichnet und muss zuletzt kommen

def my_method(&my_block)
  my_block.call(self)
end

Am häufigsten wird das Block-Argument verwendet, um einen Block an eine andere Methode zu übergeben

def each_item(&block)
  @items.each(&block)
end

Sie müssen dem Block keinen Namen geben, wenn Sie ihn nur an eine andere Methode übergeben werden

def each_item(&)
  @items.each(&)
end

Wenn Sie den Block nur aufrufen und ihn nicht anderweitig manipulieren oder an eine andere Methode senden, wird die Verwendung von yield ohne expliziten Blockparameter bevorzugt. Diese Methode ist äquivalent zur ersten Methode in diesem Abschnitt

def my_method
  yield self
end

Argumentweiterleitung

Seit Ruby 2.7 gibt es eine Syntax für die Weiterleitung aller Argumente

def concrete_method(*positional_args, **keyword_args, &block)
  [positional_args, keyword_args, block]
end

def forwarding_method(...)
  concrete_method(...)
end

forwarding_method(1, b: 2) { puts 3 }
#=>  [[1], {:b=>2}, #<Proc:...skip...>]

Das Aufrufen mit Weiterleitung ... ist nur in Methoden verfügbar, die mit ... definiert sind.

def regular_method(arg, **kwarg)
  concrete_method(...) # Syntax error
end

Seit Ruby 3.0 können sowohl in Definitionen als auch in Aufrufen führende Argumente vor ... stehen (aber in Definitionen können dies nur Positionsargumente ohne Standardwerte sein).

def request(method, path, **headers)
  puts "#{method.upcase} #{path} #{headers}"
end

def get(...)
  request(:GET, ...) # leading argument in invoking
end

get('https://ruby-lang.de', 'Accept' => 'text/html')
# Prints: GET https://ruby-lang.de {"Accept"=>"text/html"}

def logged_get(msg, ...) # leading argument in definition
  puts "Invoking #get: #{msg}"
  get(...)
end

logged_get('Ruby site', 'https://ruby-lang.de')
# Prints:
#   Invoking #get: Ruby site
#   GET https://ruby-lang.de {}

Beachten Sie, dass das Weglassen von Klammern bei Weiterleitungsaufrufen zu unerwarteten Ergebnissen führen kann

def log(...)
  puts ...  # This would be treated as `puts()...',
            # i.e. endless range from puts result
end

log("test")
# Prints: warning: ... at EOL, should be parenthesized?
# ...and then empty line

Exception-Behandlung

Methoden haben einen impliziten Ausnahmebehandlungsblock, sodass Sie begin oder end nicht zur Ausnahmebehandlung verwenden müssen. Dies

def my_method
  begin
    # code that may raise an exception
  rescue
    # handle exception
  end
end

Kann geschrieben werden als

def my_method
  # code that may raise an exception
rescue
  # handle exception
end

Ähnlich können Sie, wenn Sie Code immer ausführen möchten, auch wenn eine Ausnahme ausgelöst wird, ensure ohne begin und end verwenden

def my_method
  # code that may raise an exception
ensure
  # code that runs even if previous code raised an exception
end

Sie können rescue auch mit ensure und/oder else kombinieren, ohne begin und end

def my_method
  # code that may raise an exception
rescue
  # handle exception
else
  # only run if no exception raised above
ensure
  # code that runs even if previous code raised an exception
end

Wenn Sie eine Ausnahme nur für einen Teil Ihrer Methode abfangen möchten, verwenden Sie begin und end. Weitere Details finden Sie auf der Seite zur Ausnahmebehandlung.