Zerlegung bezeichnet das Herauslösen der Elemente einer Sammlung wie einem Array oder Hash.
Die zerlegten Werte lassen sich dann innerhalb derselben Anweisung Variablen zuweisen.
Mit Mehrfachzuweisung kannst du in einer einzigen Anweisung mehrere Variablen zuweisen und dabei Werte zerlegen.
Damit wird dein Code kürzer und lesbarer. Dazu trennst du die Variablen, denen zugewiesen wird, mit einem Komma, zum Beispiel first, second, third = [1, 2, 3].
Der Splat-Operator (*) und der Double-Splat-Operator (**) werden häufig beim Zerlegen verwendet.
Der Splat-Operator (*) kann mehrere Arrays zu einem einzigen Array kombinieren, indem er jedes davon in ein neues gemeinsames Array zerlegt.
Der Double-Splat-Operator (**) kann mehrere Hashes zu einem einzigen Hash kombinieren, indem er jeden davon in einen neuen gemeinsamen Hash zerlegt.
Diese Syntax grenzt den Fall mehrerer Werte, die als Positionsargument akzeptiert werden, von dem Fall ab, in dem wir beliebig viele Keyword-Argumente akzeptieren.
Wird der Splat-Operator (*) ohne eine Sammlung verwendet, packt er (oder setzt er zusammen) mehrere Werte in ein Array.
Das wird bei der Mehrfachzuweisung oft genutzt, um alle „übrigen“ Elemente, die keine eigene Zuweisung haben, in einer einzigen Variablen zu sammeln.
In Ruby ist dieses Zerlegen und Zusammensetzen üblich, wenn du Methoden verwendest oder definierst, die eine beliebige Anzahl von Positions- oder Keyword-Argumenten entgegennehmen.
Du siehst dann oft, dass diese Argumente als def some_method(*arguments, **keyword_arguments) definiert und als some_method(*some_array, **some_hash) verwendet werden.
*<variable_name> und **<variable_name> solltest du nicht mit * und ** verwechseln.
Während * und ** für die Multiplikation bzw. die Potenzierung stehen, sind *<variable_name> und **<variable_name> Operatoren zum Zusammensetzen und Zerlegen.
Mit der Mehrfachzuweisung kannst du mehrere Variablen in einer Zeile zuweisen.
Trenne die Werte mit einem Komma ,:
>> a, b = 1, 2
=> [1, 2]
>> a
=> 1
Die Mehrfachzuweisung ist nicht auf einen Datentyp beschränkt:
>> x, y, z = 1, "Hello", true
=> [1, "Hello", true]
>> x
=> 1
>> y
=> 'Hello'
>> z
=> true
Mit der Mehrfachzuweisung kannst du Elemente in Arrays vertauschen. Das ist in Sortieralgorithmen recht üblich. Zum Beispiel:
>> numbers = [1, 2]
=> [1, 2]
>> numbers[0], numbers[1] = numbers[1], numbers[0]
=> [2, 1]
>> numbers
=> [2, 1]
Das ist auch als „Parallel Assignment“ bekannt und lässt sich nutzen, um eine temporäre Variable zu vermeiden.
Gibt es mehr Variablen als Werte, bekommen die überzähligen Variablen nil zugewiesen:
>> a, b, c = 1, 2
=> [1, 2]
>> b
=> 2
>> c
=> nil
Gibt es mehr Werte als Variablen, werden die überzähligen Werte ignoriert:
>> a, b, c = 1, 2, 3, 4
>> b
=> 2
>> c
=> 3
In Ruby kannst du die Elemente von Arrays/Hashes in einzelne Variablen zerlegen. Da Werte in Arrays in einer Indexreihenfolge stehen, werden sie in derselben Reihenfolge in Variablen entpackt:
>> fruits = ["apple", "banana", "cherry"]
>> x, y, z = fruits
>> x
=> "apple"
Wenn du manche Werte nicht brauchst, kannst du mit _ anzeigen, dass sie „gesammelt, aber nicht verwendet“ werden:
>> fruits = ["apple", "banana", "cherry"]
>> _, _, z = fruits
>> z
=> "cherry"
Werte aus Arrays in einem Array (auch als verschachteltes Array bekannt) zu zerlegen und zuzuweisen funktioniert genauso wie ein flaches Zerlegen. Du brauchst dafür aber einen abgegrenzten Zerlegungsausdruck (()), um den Kontext oder die Position der Werte klarzumachen:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> (a, b), (c, d) = fruits_vegetables
>> a
=> "apple"
>> d
=> "potato"
Du kannst auch nur einen Teil eines verschachtelten Arrays tief entpacken:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> a, (c, d) = fruits_vegetables
>> a
=> ["apple", "banana"]
>> c
=> "carrot"
Wenn bei der Zerlegung die Variablen falsch stehen und/oder die Anzahl der Werte nicht stimmt, bekommst du einen Syntaxfehler:
fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
(a, b), (d) = fruits_vegetables
syntax error, unexpected ')', expecting '.' or &. or :: or '['
((a, b), (d)) = fruits_vegetables
Wenn du hier experimentierst, merkst du, dass das erste Muster entscheidet, nicht die verfügbaren Werte auf der rechten Seite. Der Syntaxfehler hängt nicht an der Datenstruktur.
*) zerlegenBeim [Zerlegen eines Arrays][decomposition] kannst du den Splat-Operator (*) verwenden, um die „übrigen“ Werte einzufangen.
Das ist übersichtlicher, als das Array zu schneiden (was in manchen Situationen weniger lesbar ist).
Zum Beispiel können wir das erste Element herausnehmen und die übrigen Werte einem neuen Array ohne das erste Element zuweisen:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *last = fruits
>> x
=> "apple"
>> last
=> ["banana", "cherry", "orange", "kiwi", "melon", "mango"]
Wir können auch die Werte am Anfang und Ende des Arrays herausnehmen und dabei alle Werte dazwischen gruppieren:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *middle, y, z = fruits
>> y
=> "melon"
>> middle
=> ["banana", "cherry", "orange", "kiwi"]
* lässt sich auch beim tiefen Zerlegen verwenden:
>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>> (a, *rest), b = fruits_vegetables
>> a
=> "apple"
>> rest
=> ["banana", "melon"]
Hash zerlegenEinen Hash zu zerlegen ist etwas anders als ein Array zu zerlegen. Um einen Hash entpacken zu können, musst du ihn zuerst in ein Array umwandeln. Sonst findet kein Zerlegen statt:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory
>> x
=> {:apple=>6, :banana=>2, :cherry=>3}
>> y
=> nil
Um einen Hash in ein Array umzuwandeln, kannst du die Methode to_a verwenden:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> fruits_inventory.to_a
=> [[:apple, 6], [:banana, 2], [:cherry, 3]]
>> x, y, z = fruits_inventory.to_a
>> x
=> [:apple, 6]
Wenn du die Schlüssel entpacken willst, kannst du die Methode keys verwenden:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.keys
>> x
=> :apple
Wenn du die Werte entpacken willst, kannst du die Methode values verwenden:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.values
>> x
=> 6
Beim Zusammensetzen gruppierst du mehrere Werte zu einem Array, das einer Variablen zugewiesen wird. Das ist nützlich, wenn du Werte zerlegen, Änderungen vornehmen und die Ergebnisse dann wieder in einer Variablen zusammensetzen willst. Außerdem lassen sich damit zwei oder mehr Arrays/Hashes zusammenführen.
*) zusammensetzenEin Array kannst du mit dem Splat-Operator (*) zusammensetzen.
Dabei werden alle Werte in ein Array gepackt.
>> fruits = ["apple", "banana", "cherry"]
>> more_fruits = ["orange", "kiwi", "melon", "mango"]
# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>> combined_fruits = *fruits, *more_fruits
>> combined_fruits
=> ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
**) zusammensetzenEin Hash wird mit dem Double-Splat-Operator (**) zusammengesetzt.
Dabei werden alle Schlüssel/Wert-Paare aus einem Hash in einen anderen Hash gepackt oder zwei Hashes zusammengeführt.
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> more_fruits_inventory = {orange: 4, kiwi: 1, melon: 2, mango: 3}
# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
# then the pairs are packed into combined_fruits_inventory
>> combined_fruits_inventory
=> {:apple=>6, :banana=>2, :cherry=>3, :orange=>4, :kiwi=>1, :melon=>2, :mango=>3}
*) und Double-Splat-Operator (**) in Methoden verwendenWenn du eine Methode erstellst, die eine beliebige Anzahl von Argumenten entgegennimmt, kannst du in der Methodendefinition *arguments oder **keyword_arguments verwenden.
Mit *arguments packst du eine beliebige Anzahl von Positionsargumenten (also Nicht-Keyword-Argumenten), mit **keyword_arguments eine beliebige Anzahl von Keyword-Argumenten.
Verwendung von *arguments:
# This method is defined to take any number of positional arguments
# (Using the single line form of the definition of a method.)
>> def my_method(*arguments)= arguments
# Arguments given to the method are packed into an array
>> my_method(1, 2, 3)
=> [1, 2, 3]
>> my_method("Hello")
=> ["Hello"]
>> my_method(1, 2, 3, "Hello", "Mars")
=> [1, 2, 3, "Hello", "Mars"]
Verwendung von **keyword_arguments:
# This method is defined to take any number of keyword arguments
>> def my_method(**keyword_arguments)= keyword_arguments
# Arguments given to the method are packed into a dictionary
>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}
Wenn die definierte Methode keine Parameter für Keyword-Argumente (**keyword_arguments oder <key_word>: <value>) hat, werden die Keyword-Argumente in einen Hash gepackt und dem letzten Parameter zugewiesen.
>> def my_method(a)= a
>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}
*arguments und **keyword_arguments lassen sich auch miteinander kombinieren:
def my_method(*arguments, **keyword_arguments)
p arguments.sum
for (key, value) in keyword_arguments.to_a
p key.to_s + " = " + value.to_s
end
end
my_method(1, 2, 3, a: 1, b: 2, c: 3)
6
"a = 1"
"b = 2"
"c = 3"
Du kannst Argumente auch vor und nach *arguments schreiben, um bestimmte Positionsargumente zuzulassen.
Das funktioniert genauso wie das Zerlegen eines Arrays.
Argumente müssen in einer bestimmten Reihenfolge stehen:
def my_method(<positional_arguments>, *arguments, <positional_arguments>, <key-word_arguments>, **keyword_arguments)
Wenn du diese Reihenfolge nicht einhältst, bekommst du einen Fehler.
def my_method(a, b, *arguments)
p a
p b
p arguments
end
my_method(1, 2, 3, 4, 5)
1
2
[3, 4, 5]
Du kannst Positionsargumente sowohl vor als auch nach *arguments schreiben:
>> def my_method(a, *middle, b)= middle
>> my_method(1, 2, 3, 4, 5)
=> [2, 3, 4]
Du kannst auch Positionsargumente, *arguments, Keyword-Argumente und **keyword_arguments kombinieren:
>> def my_method(first, *many, last, a:, **keyword_arguments)
p first
p many
p last
p a
p keyword_arguments
end
>> my_method(1, 2, 3, 4, 5, a: 6, b: 7, c: 8)
1
[2, 3, 4]
5
6
{:b => 7, :c => 8}
Schreibst du die Argumente in der falschen Reihenfolge, gibt es einen Fehler:
def my_method(a:, **keyword_arguments, first, *arguments, last)
arguments
end
my_method(1, 2, 3, 4, a: 5)
syntax error, unexpected local variable or method, expecting & or '&'
... my_method(a:, **keyword_arguments, first, *arguments, last)
Mit dem Splat-Operator (*) kannst du ein Array von Argumenten in einen Methodenaufruf entpacken:
def my_method(a, b, c)
p c
p b
p a
end
numbers = [1, 2, 3]
my_method(*numbers)
3
2
1
Mit dem Double-Splat-Operator (**) kannst du einen Hash von Argumenten in einen Methodenaufruf entpacken:
def my_method(a:, b:, c:)
p c
p b
p a
end
numbers = {a: 1, b: 2, c: 3}
my_method(**numbers)
3
2
1