Διαδρομές
/
Ruby
Ruby
/
Ύλη
/
Πολλαπλή ανάθεση και αποδόμηση
Πο

Πολλαπλή ανάθεση και αποδόμηση σε Ruby

1 άσκηση

Σχετικά με την έννοια Πολλαπλή ανάθεση και αποδόμηση

Η αποδόμηση αναφέρεται στην πράξη της εξαγωγής των στοιχείων μιας συλλογής, όπως ένας 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).

Caution

Τα *<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]
Note

Αυτό είναι επίσης γνωστό ως "παράλληλη ανάθεση" και μπορεί να χρησιμοποιηθεί για να αποφύγεις μια προσωρινή μεταβλητή.

Αν υπάρχουν περισσότερες μεταβλητές από τιμές, στις επιπλέον μεταβλητές θα ανατεθεί 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

Πειραματίσου εδώ και θα παρατηρήσεις ότι καθορίζει το πρώτο μοτίβο, όχι οι διαθέσιμες τιμές στη δεξιά πλευρά. Το συντακτικό σφάλμα δεν σχετίζεται με τη δομή δεδομένων.

Αποδόμηση ενός πίνακα με τον απλό τελεστή splat (*)

Όταν [αποδομείς έναν πίνακα][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 (*)

Η σύνθεση ενός πίνακα μπορεί να γίνει με τον τελεστή 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 γίνεται με τον διπλό τελεστή 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 (*) και του διπλού τελεστή 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 για να επιτρέψεις συγκεκριμένα θεσιακά ορίσματα. Αυτό λειτουργεί με τον ίδιο τρόπο όπως η αποδόμηση ενός πίνακα.

Caution

Τα ορίσματα πρέπει να είναι διατεταγμένα με συγκεκριμένη σειρά:

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
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα

Μάθε την έννοια Πολλαπλή ανάθεση και αποδόμηση