Tracks
/
Ruby
Ruby
/
Temario
/
Asignación múltiple y descomposición
As

Asignación múltiple y descomposición en Ruby

1 ejercicio

Acerca de Asignación múltiple y descomposición

La descomposición se refiere al acto de extraer los elementos de una colección, como un Array o un Hash. Los valores descompuestos se pueden asignar después a variables dentro de la misma sentencia.

La asignación múltiple es la capacidad de asignar varias variables para descomponer valores dentro de una sola sentencia. Esto permite que el código sea más conciso y legible, y se hace separando con una coma las variables que se van a asignar, por ejemplo first, second, third = [1, 2, 3].

El operador splat (*) y el operador splat doble (**) se usan con frecuencia en contextos de descomposición. El operador splat (*) se puede usar para combinar varios arrays en un solo array al descomponer cada uno en un nuevo array común. El operador splat doble (**) se puede usar para combinar varios hashes en un solo hash al descomponer cada uno en un nuevo hash común. Esta es una sintaxis que sirve para diferenciar entre varios valores aceptados como argumento posicional y un caso en el que aceptamos cualquier cantidad de argumentos de palabra clave.

Cuando el operador splat (*) se usa sin una colección, empaqueta (o compone) una serie de valores en un array. Esto se usa a menudo en la asignación múltiple para agrupar en una sola variable todos los elementos «restantes» que no tienen una asignación individual.

En Ruby es común usar este comportamiento de descomposición/composición al usar o definir métodos que reciben una cantidad arbitraria de argumentos posicionales o de palabra clave. A menudo verás estos argumentos definidos como def some_method(*arguments, **keyword_arguments) y usados como some_method(*some_array, **some_hash).

Caution

*<variable_name> y **<variable_name> no deben confundirse con * y **. Mientras que * y ** se usan para la multiplicación y la exponenciación, respectivamente, *<variable_name> y **<variable_name> se usan como operadores de composición y descomposición.

Asignación múltiple

La asignación múltiple te permite asignar varias variables en una sola línea. Para separar los valores, usa una coma ,:

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

La asignación múltiple no se limita a un solo tipo de dato:

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

La asignación múltiple se puede usar para intercambiar elementos en arrays. Esta práctica es bastante común en los algoritmos de ordenamiento. Por ejemplo:

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

Esto también se conoce como «Parallel Assignment» y se puede usar para evitar una variable temporal.

Si hay más variables que valores, a las variables adicionales se les asignará nil:

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

Si hay más valores que variables, los valores adicionales se ignorarán:

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

Descomposición

En Ruby, es posible descomponer los elementos de arrays/hashes en variables distintas. Como los valores aparecen dentro de los arrays en un orden de índice, se desempaquetan en las variables en ese mismo orden:

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

Si hay valores que no necesitas, puedes usar _ para indicar «recogido pero no usado»:

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

Descomposición profunda

Descomponer y asignar valores de arrays dentro de un array (también conocido como array anidado) funciona igual que una descomposición superficial, pero necesita una expresión de descomposición delimitada (()) para aclarar el contexto o la posición de los valores:

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

También puedes desempaquetar en profundidad solo una parte de un array anidado:

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

Si la descomposición tiene variables con una ubicación incorrecta o una cantidad incorrecta de valores, obtendrás un error de sintaxis:

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

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

Experimenta aquí y notarás que lo que manda es el primer patrón, no los valores disponibles del lado derecho. El error de sintaxis no está ligado a la estructura de datos.

Descomponer un array con el operador splat simple (*)

Cuando [descompones un array][decomposition] puedes usar el operador splat (*) para capturar los valores «sobrantes». Esto es más claro que cortar el array (que en algunas situaciones es menos legible). Por ejemplo, podemos extraer el primer elemento y luego asignar los valores restantes a un nuevo array sin ese primer elemento:

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

También podemos extraer los valores al principio y al final del array mientras agrupamos todos los valores de en medio:

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

También podemos usar * en la descomposición profunda:

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

Descomponer un Hash

Descomponer un hash es un poco distinto de descomponer un array. Para poder desempaquetar un hash, primero debes convertirlo en un array. De lo contrario, no habrá descomposición:

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

Para convertir un Hash en un array puedes usar el método 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]

Si quieres desempaquetar las claves, puedes usar el método keys:

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

Si quieres desempaquetar los valores, puedes usar el método values:

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

Composición

Componer es la capacidad de agrupar varios valores en un solo array que se asigna a una variable. Esto es útil cuando quieres descomponer valores, hacer cambios y luego componer los resultados de vuelta en una variable. También hace posible combinar 2 o más arrays/hashes.

Componer un array con el operador splat (*)

Componer un array se puede hacer con el operador splat (*). Esto empaquetará todos los valores en 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"]

Componer un hash con el operador splat doble (**)

Componer un hash se hace con el operador splat doble (**). Esto empaquetará todos los pares clave/valor de un hash en otro hash, o combinará dos hashes entre sí.

>> 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 del operador splat (*) y del operador splat doble (**) con métodos

Composición con parámetros de método

Cuando creas un método que acepta una cantidad arbitraria de argumentos, puedes usar *arguments o **keyword_arguments en la definición del método. *arguments se usa para empaquetar una cantidad arbitraria de argumentos posicionales (sin palabra clave) y **keyword_arguments se usa para empaquetar una cantidad arbitraria de argumentos de palabra clave.

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

Si el método definido no tiene ningún parámetro definido para argumentos de palabra clave (**keyword_arguments o <key_word>: <value>), los argumentos de palabra clave se empaquetarán en un hash y se asignarán al último parámetro.

>> def my_method(a)= a

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

*arguments y **keyword_arguments también se pueden usar en combinación uno con otro:

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"

También puedes escribir argumentos antes y después de *arguments para permitir argumentos posicionales específicos. Esto funciona igual que descomponer un array.

Caution

Los argumentos deben estructurarse en un orden específico:

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

Si no sigues este orden, obtendrás un error.

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]

Puedes escribir argumentos posicionales antes y después de *arguments:

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

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

También puedes combinar argumentos posicionales, *arguments, argumentos de palabra clave y **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}

Escribir los argumentos en un orden incorrecto dará como resultado un error:

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)

Descomponer en llamadas a métodos

Puedes usar el operador splat (*) para desempaquetar un array de argumentos en una llamada a método:

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

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

También puedes usar el operador splat doble (**) para desempaquetar un hash de argumentos en una llamada a método:

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
Editar en GitHub El enlace se abre en una ventana o pestaña nueva

Aprende Asignación múltiple y descomposición