Tracce
/
Ruby
Ruby
/
Programma
/
Assegnazione multipla e scomposizione
As

Assegnazione multipla e scomposizione in Ruby

1 esercizio

Informazioni su Assegnazione multipla e scomposizione

Con decomposizione si intende l'atto di estrarre gli elementi di una collezione, come un Array o un Hash. I valori decomposti possono poi essere assegnati a delle variabili all'interno della stessa istruzione.

L'assegnazione multipla è la possibilità di assegnare più variabili per decomporre dei valori all'interno di una sola istruzione. Questo permette di rendere il codice più conciso e leggibile, e si ottiene separando con una virgola le variabili da assegnare, come in first, second, third = [1, 2, 3].

L'operatore splat (*) e il doppio splat (**) vengono spesso usati in contesti di decomposizione. L'operatore splat (*) può essere usato per combinare più array in un unico array, decomponendo ciascuno in un nuovo array comune. Il doppio splat (**) può essere usato per combinare più hash in un unico hash, decomponendo ciascuno in un nuovo hash comune. Questa sintassi serve a distinguere più valori accettati come argomento posizionale dal caso in cui se ne accettano uno o più come argomenti con parola chiave.

Quando l'operatore splat (*) viene usato senza una collezione, impacchetta (o compone) una serie di valori in un array. Nell'assegnazione multipla questo è spesso usato per raggruppare in un'unica variabile tutti gli elementi «rimanenti» che non hanno un'assegnazione individuale.

In Ruby è comune ricorrere a questo comportamento di decomposizione e composizione quando si usano o si definiscono metodi che accettano un numero arbitrario di argomenti posizionali o con parola chiave. Spesso vedrai questi argomenti definiti come def some_method(*arguments, **keyword_arguments) e usati come some_method(*some_array, **some_hash).

Caution

*<variable_name> e **<variable_name> non vanno confusi con * e **. Mentre * e ** sono usati rispettivamente per la moltiplicazione e l'elevamento a potenza, *<variable_name> e **<variable_name> sono usati come operatori di composizione e decomposizione.

Assegnazione multipla

L'assegnazione multipla ti permette di assegnare più variabili in una sola riga. Per separare i valori, usa una virgola ,:

>> a, b = 1, 2
=> [1, 2]
>> a
=> 1

L'assegnazione multipla non è limitata a un solo tipo di dato:

>> x, y, z = 1, "Hello", true
=> [1, "Hello", true]
>> x
=> 1
>> y
=> 'Hello'
>> z
=> true

L'assegnazione multipla può essere usata per scambiare gli elementi in array. Questa pratica è piuttosto comune negli algoritmi di ordinamento. Per esempio:

>> numbers = [1, 2]
=> [1, 2]
>> numbers[0], numbers[1] = numbers[1], numbers[0]
=> [2, 1]
>> numbers
=> [2, 1]
Note

Questa tecnica è nota anche come «Parallel Assignment» e può essere usata per evitare una variabile temporanea.

Se ci sono più variabili che valori, alle variabili in eccesso verrà assegnato nil:

>> a, b, c = 1, 2
=> [1, 2]
>> b
=> 2
>> c
=> nil

Se ci sono più valori che variabili, i valori in eccesso verranno ignorati:

>> a, b, c = 1, 2, 3, 4
>> b
=> 2
>> c
=> 3

Decomposizione

In Ruby è possibile decomporre gli elementi di array/hash in variabili distinte. Dato che i valori compaiono all'interno degli array secondo un ordine di indice, vengono assegnati alle variabili nello stesso ordine:

>> fruits = ["apple", "banana", "cherry"]
>> x, y, z = fruits
>> x
=> "apple"

Se ci sono valori che non ti servono, puoi usare _ per indicare «raccolto ma non usato»:

>> fruits = ["apple", "banana", "cherry"]
>> _, _, z = fruits
>> z
=> "cherry"

Decomposizione profonda

Decomporre e assegnare i valori di array contenuti dentro un array (noto anche come array annidato) funziona allo stesso modo di una decomposizione superficiale, ma richiede un'espressione di decomposizione delimitata (()) per chiarire il contesto o la posizione dei valori:

>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> (a, b), (c, d) = fruits_vegetables
>> a
=> "apple"
>> d
=> "potato"

Puoi anche estrarre in profondità solo una parte di un array annidato:

>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> a, (c, d) = fruits_vegetables
>> a
=> ["apple", "banana"]
>> c
=> "carrot"

Se la decomposizione ha variabili posizionate in modo errato e/o un numero errato di valori, otterrai un errore di sintassi:

fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
(a, b), (d) = fruits_vegetables

syntax error, unexpected ')', expecting '.' or &. or :: or '['
((a, b), (d)) = fruits_vegetables

Fai qualche esperimento qui e noterai che è il primo schema a dettare le regole, non i valori disponibili sul lato destro. L'errore di sintassi non è legato alla struttura dati.

Decomporre un array con l'operatore splat singolo (*)

Quando [decomponi un array][decomposition] puoi usare l'operatore splat (*) per catturare i valori «avanzati». Questo è più chiaro che ritagliare l'array (che in alcune situazioni è meno leggibile). Per esempio, possiamo estrarre il primo elemento e poi assegnare i valori rimanenti a un nuovo array senza il primo elemento:

>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *last = fruits
>> x
=> "apple"
>> last
=> ["banana", "cherry", "orange", "kiwi", "melon", "mango"]

Possiamo anche estrarre i valori all'inizio e alla fine dell'array, raggruppando tutti i valori centrali:

>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *middle, y, z = fruits
>> y
=> "melon"
>> middle
=> ["banana", "cherry", "orange", "kiwi"]

Possiamo anche usare * nella decomposizione profonda:

>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>> (a, *rest), b = fruits_vegetables
>> a
=> "apple"
>> rest
=> ["banana", "melon"]

Decomporre un Hash

Decomporre un hash è un po' diverso dal decomporre un array. Per poter estrarre i valori da un hash, devi prima convertirlo in un array. Altrimenti non ci sarà alcuna decomposizione:

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory
>> x
=> {:apple=>6, :banana=>2, :cherry=>3}
>> y
=> nil

Per convertire un Hash in un array puoi usare il metodo 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]

Se vuoi estrarre le chiavi, puoi usare il metodo keys:

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.keys
>> x
=> :apple

Se vuoi estrarre i valori, puoi usare il metodo values:

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.values
>> x
=> 6

Composizione

Comporre è la capacità di raggruppare più valori in un unico array che viene assegnato a una variabile. È utile quando vuoi decomporre dei valori, apportare delle modifiche e poi comporre di nuovo i risultati in una variabile. Rende inoltre possibile eseguire delle fusioni su due o più array/hash.

Comporre un array con l'operatore splat (*)

Comporre un array si può fare usando l'operatore splat (*). Questo impacchetterà tutti i valori in un array.

>> 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"]

Comporre un hash con il doppio splat (**)

Comporre un hash si fa usando il doppio splat (**). Questo impacchetterà tutte le coppie chiave/valore da un hash a un altro hash, oppure combinerà due hash insieme.

>> 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}

Uso dell'operatore splat (*) e del doppio splat (**) con i metodi

Composizione con i parametri di un metodo

Quando crei un metodo che accetta un numero arbitrario di argomenti, puoi usare *arguments o **keyword_arguments nella definizione del metodo. *arguments serve a impacchettare un numero arbitrario di argomenti posizionali (senza parola chiave) e **keyword_arguments serve a impacchettare un numero arbitrario di argomenti con parola chiave.

Uso di *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"]

Uso di **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}

Se il metodo definito non ha alcun parametro definito per gli argomenti con parola chiave (**keyword_arguments o <key_word>: <value>), allora gli argomenti con parola chiave verranno impacchettati in un hash e assegnati all'ultimo parametro.

>> def my_method(a)= a

>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}

*arguments e **keyword_arguments possono anche essere usati in combinazione tra loro:

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"

Puoi anche scrivere argomenti prima e dopo *arguments per permettere argomenti posizionali specifici. Funziona allo stesso modo della decomposizione di un array.

Caution

Gli argomenti devono essere strutturati in un ordine specifico:

def my_method(<positional_arguments>, *arguments, <positional_arguments>, <key-word_arguments>, **keyword_arguments)

Se non segui questo ordine, otterrai un errore.

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]

Puoi scrivere argomenti posizionali prima e dopo *arguments:

>> def my_method(a, *middle, b)= middle

>> my_method(1, 2, 3, 4, 5)
=> [2, 3, 4]

Puoi anche combinare argomenti posizionali, *arguments, argomenti con parola chiave e **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}

Scrivere gli argomenti in un ordine errato causerà un errore:

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)

Decomporre in chiamate di metodo

Puoi usare l'operatore splat (*) per estrarre in una chiamata di metodo gli argomenti contenuti in un array:

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

numbers = [1, 2, 3]
my_method(*numbers)
3
2
1

Puoi anche usare il doppio splat (**) per estrarre in una chiamata di metodo gli argomenti contenuti in un 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
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Assegnazione multipla e scomposizione