Η αποδόμηση αναφέρεται στην πράξη της εξαγωγής των στοιχείων μιας συλλογής, όπως ένας Array ή ένας Hash.
Οι αποδομημένες τιμές μπορούν στη συνέχεια να ανατεθούν σε μεταβλητές μέσα στην ίδια εντολή.
Η πολλαπλή ανάθεση είναι η δυνατότητα να αναθέτεις πολλαπλές μεταβλητές για να αποδομήσεις τιμές μέσα σε μία εντολή.
Αυτό επιτρέπει στον κώδικα να είναι πιο συνοπτικός και ευανάγνωστος, και γίνεται χωρίζοντας τις μεταβλητές που θα ανατεθούν με κόμμα, όπως first, second, third = [1, 2, 3].
Ο τελεστής splat (*) και ο διπλός τελεστής splat (**) χρησιμοποιούνται συχνά σε περιβάλλοντα αποδόμησης.
Ο τελεστής splat (*) μπορεί να χρησιμοποιηθεί για να συνδυάσει πολλούς πίνακες σε έναν πίνακα, αποδομώντας τον καθένα σε έναν νέο κοινό πίνακα.
Ο διπλός τελεστής splat (**) μπορεί να χρησιμοποιηθεί για να συνδυάσει πολλά hash σε ένα hash, αποδομώντας το καθένα σε ένα νέο κοινό hash.
Αυτή είναι σύνταξη που χρησιμοποιείται για να διακρίνει πολλαπλές τιμές που γίνονται δεκτές ως θεσιακό όρισμα από την περίπτωση όπου δεχόμαστε οποιοδήποτε πλήθος ορισμάτων με λέξεις-κλειδιά.
Όταν ο τελεστής splat (*) χρησιμοποιείται χωρίς συλλογή, συσκευάζει (ή συνθέτει) έναν αριθμό τιμών σε έναν πίνακα.
Αυτό χρησιμοποιείται συχνά στην πολλαπλή ανάθεση για να ομαδοποιήσει όλα τα "υπόλοιπα" στοιχεία που δεν έχουν δική τους ανάθεση σε μία μόνο μεταβλητή.
Στη Ruby είναι συνηθισμένο να χρησιμοποιείς αυτή τη συμπεριφορά αποδόμησης/σύνθεσης όταν χρησιμοποιείς ή ορίζεις μεθόδους που δέχονται οποιοδήποτε πλήθος θεσιακών ορισμάτων ή ορισμάτων με λέξεις-κλειδιά.
Θα δεις συχνά αυτά τα ορίσματα να ορίζονται ως def some_method(*arguments, **keyword_arguments) και τα ορίσματα να χρησιμοποιούνται ως some_method(*some_array, **some_hash).
Τα *<variable_name> και **<variable_name> δεν πρέπει να συγχέονται με τα * και **.
Ενώ τα * και ** χρησιμοποιούνται για πολλαπλασιασμό και ύψωση σε δύναμη αντίστοιχα, τα *<variable_name> και **<variable_name> χρησιμοποιούνται ως τελεστές σύνθεσης και αποδόμησης.
Η πολλαπλή ανάθεση σου επιτρέπει να αναθέσεις πολλαπλές μεταβλητές σε μία γραμμή.
Για να χωρίσεις τις τιμές, χρησιμοποίησε κόμμα ,:
>> a, b = 1, 2
=> [1, 2]
>> a
=> 1
Η πολλαπλή ανάθεση δεν περιορίζεται σε έναν μόνο τύπο δεδομένων:
>> x, y, z = 1, "Hello", true
=> [1, "Hello", true]
>> x
=> 1
>> y
=> 'Hello'
>> z
=> true
Η πολλαπλή ανάθεση μπορεί να χρησιμοποιηθεί για να ανταλλάξεις στοιχεία σε πίνακες. Αυτή η πρακτική είναι αρκετά συνηθισμένη στους αλγορίθμους ταξινόμησης. Για παράδειγμα:
>> numbers = [1, 2]
=> [1, 2]
>> numbers[0], numbers[1] = numbers[1], numbers[0]
=> [2, 1]
>> numbers
=> [2, 1]
Αυτό είναι επίσης γνωστό ως "παράλληλη ανάθεση" και μπορεί να χρησιμοποιηθεί για να αποφύγεις μια προσωρινή μεταβλητή.
Αν υπάρχουν περισσότερες μεταβλητές από τιμές, στις επιπλέον μεταβλητές θα ανατεθεί nil:
>> a, b, c = 1, 2
=> [1, 2]
>> b
=> 2
>> c
=> nil
Αν υπάρχουν περισσότερες τιμές από μεταβλητές, οι επιπλέον τιμές θα αγνοηθούν:
>> a, b, c = 1, 2, 3, 4
>> b
=> 2
>> c
=> 3
Στη Ruby είναι δυνατό να αποδομήσεις τα στοιχεία πινάκων/hash σε ξεχωριστές μεταβλητές. Εφόσον οι τιμές εμφανίζονται μέσα σε πίνακες με σειρά θέσης, αποσυσκευάζονται σε μεταβλητές με την ίδια σειρά:
>> fruits = ["apple", "banana", "cherry"]
>> x, y, z = fruits
>> x
=> "apple"
Αν υπάρχουν τιμές που δεν χρειάζεσαι, τότε μπορείς να χρησιμοποιήσεις το _ για να δηλώσεις "συλλέχθηκε αλλά δεν χρησιμοποιείται":
>> fruits = ["apple", "banana", "cherry"]
>> _, _, z = fruits
>> z
=> "cherry"
Η αποδόμηση και η ανάθεση τιμών από πίνακες που βρίσκονται μέσα σε έναν πίνακα (_γνωστό και ως εμφωλευμένο πίνακα), λειτουργεί με τον ίδιο τρόπο όπως η ρηχή αποδόμηση, αλλά χρειάζεται έκφραση αποδόμησης με οριοθέτες (()) για να διευκρινίσει το περιβάλλον ή τη θέση των τιμών:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> (a, b), (c, d) = fruits_vegetables
>> a
=> "apple"
>> d
=> "potato"
Μπορείς επίσης να αποσυσκευάσεις βαθιά μόνο ένα μέρος ενός εμφωλευμένου πίνακα:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> a, (c, d) = fruits_vegetables
>> a
=> ["apple", "banana"]
>> c
=> "carrot"
Αν η αποδόμηση έχει μεταβλητές με λανθασμένη τοποθέτηση ή/και λανθασμένο αριθμό τιμών, θα πάρεις ένα συντακτικό σφάλμα:
fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
(a, b), (d) = fruits_vegetables
syntax error, unexpected ')', expecting '.' or &. or :: or '['
((a, b), (d)) = fruits_vegetables
Πειραματίσου εδώ και θα παρατηρήσεις ότι καθορίζει το πρώτο μοτίβο, όχι οι διαθέσιμες τιμές στη δεξιά πλευρά. Το συντακτικό σφάλμα δεν σχετίζεται με τη δομή δεδομένων.
*)Όταν [αποδομείς έναν πίνακα][decomposition], μπορείς να χρησιμοποιήσεις τον τελεστή splat (*) για να συλλάβεις τις "υπόλοιπες" τιμές.
Αυτό είναι πιο ξεκάθαρο από το να κόψεις τον πίνακα (κάτι που σε ορισμένες περιπτώσεις είναι λιγότερο ευανάγνωστο).
Για παράδειγμα, μπορούμε να εξαγάγουμε το πρώτο στοιχείο και μετά να αναθέσουμε τις υπόλοιπες τιμές σε έναν νέο πίνακα χωρίς το πρώτο στοιχείο:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *last = fruits
>> x
=> "apple"
>> last
=> ["banana", "cherry", "orange", "kiwi", "melon", "mango"]
Μπορούμε επίσης να εξαγάγουμε τις τιμές στην αρχή και στο τέλος του πίνακα, ομαδοποιώντας όλες τις τιμές στη μέση:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *middle, y, z = fruits
>> y
=> "melon"
>> middle
=> ["banana", "cherry", "orange", "kiwi"]
Μπορούμε επίσης να χρησιμοποιήσουμε το * στη βαθιά αποδόμηση:
>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>> (a, *rest), b = fruits_vegetables
>> a
=> "apple"
>> rest
=> ["banana", "melon"]
Hash
Η αποδόμηση ενός hash είναι λίγο διαφορετική από την αποδόμηση ενός πίνακα. Για να μπορέσεις να αποσυσκευάσεις ένα hash, πρέπει πρώτα να το μετατρέψεις σε πίνακα. Διαφορετικά, δεν θα γίνει καμία αποδόμηση:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory
>> x
=> {:apple=>6, :banana=>2, :cherry=>3}
>> y
=> nil
Για να μετατρέψεις ένα Hash σε πίνακα, μπορείς να χρησιμοποιήσεις τη μέθοδο to_a:
>> 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]
Αν θέλεις να αποσυσκευάσεις τα κλειδιά, τότε μπορείς να χρησιμοποιήσεις τη μέθοδο keys:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.keys
>> x
=> :apple
Αν θέλεις να αποσυσκευάσεις τις τιμές, τότε μπορείς να χρησιμοποιήσεις τη μέθοδο values:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.values
>> x
=> 6
Η σύνθεση είναι η δυνατότητα να ομαδοποιείς πολλαπλές τιμές σε έναν πίνακα που ανατίθεται σε μια μεταβλητή. Αυτό είναι χρήσιμο όταν θέλεις να αποδομήσεις τιμές, να κάνεις αλλαγές και μετά να συνθέσεις τα αποτελέσματα πάλι σε μια μεταβλητή. Κάνει επίσης δυνατή τη συγχώνευση 2 ή περισσότερων πινάκων/hash.
*)Η σύνθεση ενός πίνακα μπορεί να γίνει με τον τελεστή splat (*).
Αυτό θα συσκευάσει όλες τις τιμές σε έναν πίνακα.
>> 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"]
**)Η σύνθεση ενός hash γίνεται με τον διπλό τελεστή splat (**).
Αυτό θα συσκευάσει όλα τα ζεύγη κλειδιού/τιμής από ένα hash σε ένα άλλο hash ή θα συνδυάσει δύο hash μαζί.
>> 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}
*) και του διπλού τελεστή splat (**) με μεθόδουςΌταν δημιουργείς μια μέθοδο που δέχεται οποιοδήποτε πλήθος ορισμάτων, μπορείς να χρησιμοποιήσεις το *arguments ή το **keyword_arguments στον ορισμό της μεθόδου.
Το *arguments χρησιμοποιείται για να συσκευάσει οποιοδήποτε πλήθος θεσιακών (χωρίς λέξεις-κλειδιά) ορισμάτων και το **keyword_arguments χρησιμοποιείται για να συσκευάσει οποιοδήποτε πλήθος ορισμάτων με λέξεις-κλειδιά.
Χρήση του *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"]
Χρήση του **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}
Αν η μέθοδος που ορίστηκε δεν έχει καμία ορισμένη παράμετρο για ορίσματα με λέξεις-κλειδιά (**keyword_arguments ή <key_word>: <value>), τότε τα ορίσματα με λέξεις-κλειδιά θα συσκευαστούν σε ένα hash και θα ανατεθούν στην τελευταία παράμετρο.
>> def my_method(a)= a
>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}
Τα *arguments και **keyword_arguments μπορούν επίσης να χρησιμοποιηθούν σε συνδυασμό μεταξύ τους:
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"
Μπορείς επίσης να γράψεις ορίσματα πριν και μετά το *arguments για να επιτρέψεις συγκεκριμένα θεσιακά ορίσματα.
Αυτό λειτουργεί με τον ίδιο τρόπο όπως η αποδόμηση ενός πίνακα.
Τα ορίσματα πρέπει να είναι διατεταγμένα με συγκεκριμένη σειρά:
def my_method(<positional_arguments>, *arguments, <positional_arguments>, <key-word_arguments>, **keyword_arguments)
Αν δεν ακολουθήσεις αυτή τη σειρά, τότε θα πάρεις ένα σφάλμα.
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]
Μπορείς να γράψεις θεσιακά ορίσματα πριν και μετά το *arguments:
>> def my_method(a, *middle, b)= middle
>> my_method(1, 2, 3, 4, 5)
=> [2, 3, 4]
Μπορείς επίσης να συνδυάσεις θεσιακά ορίσματα, *arguments, ορίσματα με λέξεις-κλειδιά και **keyword_arguments:
>> 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}
Το να γράψεις τα ορίσματα με λανθασμένη σειρά θα προκαλέσει σφάλμα:
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)
Μπορείς να χρησιμοποιήσεις τον τελεστή splat (*) για να αποσυσκευάσεις έναν πίνακα ορισμάτων σε μια κλήση μεθόδου:
def my_method(a, b, c)
p c
p b
p a
end
numbers = [1, 2, 3]
my_method(*numbers)
3
2
1
Μπορείς επίσης να χρησιμοποιήσεις τον διπλό τελεστή splat (**) για να αποσυσκευάσεις ένα hash ορισμάτων σε μια κλήση μεθόδου:
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