La descomposición hace referencia 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 instrucción.
La asignación múltiple es la capacidad de asignar varias variables para descomponer valores dentro de una sola instrucción.
Esto permite que el código sea más conciso y legible, y se consigue separando con una coma las variables que se van a asignar, como en first, second, third = [1, 2, 3].
El operador splat (*) y el operador doble splat (**) se usan a menudo en contextos de descomposición.
El operador splat (*) se puede usar para combinar varios arrays en un solo array descomponiendo cada uno en un nuevo array común.
El operador doble splat (**) se puede usar para combinar varios hashes en un solo hash descomponiendo cada uno en un nuevo hash común.
Esta es la sintaxis que se usa 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) varios 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 habitual usar este comportamiento de descomposición/composición al usar o definir métodos que aceptan un número arbitrario 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).
*<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.
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 datos:
>> 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 ordenación. Por ejemplo:
>> numbers = [1, 2]
=> [1, 2]
>> numbers[0], numbers[1] = numbers[1], numbers[0]
=> [2, 1]
>> numbers
=> [2, 1]
Esto también se conoce como «asignación paralela» y se puede usar para evitar una variable temporal.
Si hay más variables que valores, a las variables sobrantes se les asignará nil:
>> a, b, c = 1, 2
=> [1, 2]
>> b
=> 2
>> c
=> nil
Si hay más valores que variables, los valores sobrantes se ignorarán:
>> a, b, c = 1, 2, 3, 4
>> b
=> 2
>> c
=> 3
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 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"
Descomponer y asignar valores de arrays que están dentro de otro 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 colocadas de forma incorrecta o un número incorrecto 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 te darás cuenta de 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.
*)Al [descomponer 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 el 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 del principio y del final del array y agrupar 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"]
Hash
Descomponer un hash es un poco distinto de descomponer un array. Para poder desempaquetar un hash, primero tienes que 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
La composición 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 nuevo en una variable. También hace posible combinar 2 o más arrays/hashes.
*)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 se hace usando el operador doble splat (**).
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}
*) y del operador doble splat (**) con métodosCuando creas un método que acepta un número arbitrario de argumentos, puedes usar *arguments o **keyword_arguments en la definición del método.
*arguments se usa para empaquetar un número arbitrario de argumentos posicionales (sin palabra clave) y **keyword_arguments se usa para empaquetar un número arbitrario 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 parámetros definidos 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 combinados entre sí:
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 concretos.
Esto funciona igual que descomponer un array.
Los argumentos tienen que estructurarse en un orden concreto:
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á lugar a 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)
Puedes usar el operador splat (*) para desempaquetar un array de argumentos en una llamada a un 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 doble splat (**) para desempaquetar un hash de argumentos en una llamada a un 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