Decomposição é o ato de extrair os elementos de uma coleção, como um Array ou um Hash.
Os valores decompostos podem então ser atribuídos a variáveis na mesma instrução.
A atribui��ão múltipla é a capacidade de atribuir várias variáveis a valores decompostos em uma única instrução.
Isso deixa o código mais conciso e legível, e é feito separando as variáveis a serem atribuídas com uma vírgula, como em first, second, third = [1, 2, 3].
O operador splat (*) e o operador splat duplo (**) são usados com frequência em contextos de decomposição.
O operador splat (*) pode ser usado para combinar vários arrays em um único array, decompondo cada um em um novo array comum.
O operador splat duplo (**) pode ser usado para combinar vários hashes em um único hash, decompondo cada um em um novo hash comum.
Essa é a sintaxe usada para diferenciar o caso de vários valores aceitos como um argumento posicional do caso em que aceitamos quantos argumentos de palavra-chave quisermos.
Quando o operador splat (*) é usado sem uma coleção, ele empacota (ou compõe) uma série de valores em um array.
Isso é usado com frequência na atribuição múltipla para agrupar todos os elementos "restantes", que não têm atribuições individuais, em uma única variável.
É comum no Ruby usar esse comportamento de decomposição/composição ao usar ou definir métodos que recebem um número arbitrário de argumentos posicionais ou de palavra-chave.
Você vai ver esses argumentos definidos como def some_method(*arguments, **keyword_arguments) e usados como some_method(*some_array, **some_hash).
*<variable_name> e **<variable_name> não devem ser confundidos com * e **.
Enquanto * e ** são usados para multiplicação e exponenciação, respectivamente, *<variable_name> e **<variable_name> são usados como operadores de composição e decomposição.
A atribuição múltipla permite atribuir várias variáveis em uma linha.
Para separar os valores, use uma vírgula ,:
>> a, b = 1, 2
=> [1, 2]
>> a
=> 1
A atribuição múltipla não se limita a um único tipo de dado:
>> x, y, z = 1, "Hello", true
=> [1, "Hello", true]
>> x
=> 1
>> y
=> 'Hello'
>> z
=> true
A atribuição múltipla pode ser usada para trocar elementos em arrays. Essa prática é bem comum em algoritmos de ordenação. Por exemplo:
>> numbers = [1, 2]
=> [1, 2]
>> numbers[0], numbers[1] = numbers[1], numbers[0]
=> [2, 1]
>> numbers
=> [2, 1]
Isso também é conhecido como "Parallel Assignment" e pode ser usado para evitar uma variável temporária.
Se houver mais variáveis do que valores, as variáveis extras vão receber nil:
>> a, b, c = 1, 2
=> [1, 2]
>> b
=> 2
>> c
=> nil
Se houver mais valores do que variáveis, os valores extras serão ignorados:
>> a, b, c = 1, 2, 3, 4
>> b
=> 2
>> c
=> 3
No Ruby, é possível decompor os elementos de arrays/hashes em variáveis distintas. Como os valores aparecem dentro de um array em ordem de índice, eles são desempacotados nas variáveis na mesma ordem:
>> fruits = ["apple", "banana", "cherry"]
>> x, y, z = fruits
>> x
=> "apple"
Se houver valores que não são necessários, você pode usar _ para indicar "coletado mas não usado":
>> fruits = ["apple", "banana", "cherry"]
>> _, _, z = fruits
>> z
=> "cherry"
Decompor e atribuir valores de arrays que estão dentro de outro array (também conhecido como array aninhado) funciona do mesmo jeito que uma decomposição superficial, mas precisa de uma expressão de decomposição delimitada (()) para deixar claro o contexto ou a posição dos valores:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> (a, b), (c, d) = fruits_vegetables
>> a
=> "apple"
>> d
=> "potato"
Você também pode desempacotar profundamente apenas uma parte de um array aninhado:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> a, (c, d) = fruits_vegetables
>> a
=> ["apple", "banana"]
>> c
=> "carrot"
Se a decomposição tiver variáveis com posicionamento incorreto e/ou um número incorreto de valores, você vai receber um erro de sintaxe:
fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
(a, b), (d) = fruits_vegetables
syntax error, unexpected ')', expecting '.' or &. or :: or '['
((a, b), (d)) = fruits_vegetables
Experimente aqui e você vai perceber que quem manda é o primeiro padrão, e não os valores disponíveis do lado direito. O erro de sintaxe não está ligado à estrutura de dados.
*)Ao [decompor um array][decomposition], você pode usar o operador splat (*) para capturar os valores "restantes".
Isso é mais claro do que fatiar o array (o que, em algumas situações, é menos legível).
Por exemplo, podemos extrair o primeiro elemento e depois atribuir os valores restantes a um novo array sem o primeiro elemento:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *last = fruits
>> x
=> "apple"
>> last
=> ["banana", "cherry", "orange", "kiwi", "melon", "mango"]
Também podemos extrair os valores do início e do fim do array e agrupar todos os valores do meio:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *middle, y, z = fruits
>> y
=> "melon"
>> middle
=> ["banana", "cherry", "orange", "kiwi"]
Também podemos usar * na decomposição profunda:
>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>> (a, *rest), b = fruits_vegetables
>> a
=> "apple"
>> rest
=> ["banana", "melon"]
Hash
Decompor um hash é um pouco diferente de decompor um array. Para conseguir desempacotar um hash, você precisa primeiro convertê-lo em um array. Caso contrário, não haverá decomposição:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory
>> x
=> {:apple=>6, :banana=>2, :cherry=>3}
>> y
=> nil
Para converter um Hash em um array, você pode usar o 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]
Se você quiser desempacotar as chaves, pode usar o método keys:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.keys
>> x
=> :apple
Se você quiser desempacotar os valores, pode usar o método values:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.values
>> x
=> 6
Compor é a capacidade de agrupar vários valores em um único array que é atribuído a uma variável. Isso é útil quando você quer decompor valores, fazer alterações e depois compor os resultados de volta em uma variável. Também torna possível fazer mesclagens de 2 ou mais arrays/hashes.
*)Compor um array pode ser feito usando o operador splat (*).
Ele empacota todos os valores em um 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"]
**)Compor um hash é feito usando o operador splat duplo (**).
Ele empacota todos os pares chave/valor de um hash em outro hash, ou combina dois hashes.
>> 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}
*) e do operador splat duplo (**) com métodosQuando você cria um método que aceita um número arbitrário de argumentos, pode usar *arguments ou **keyword_arguments na definição do método.
*arguments é usado para empacotar um número arbitrário de argumentos posicionais (sem palavra-chave) e **keyword_arguments é usado para empacotar um número arbitrário de argumentos de palavra-chave.
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}
Se o método definido não tiver nenhum parâmetro definido para argumentos de palavra-chave (**keyword_arguments ou <key_word>: <value>), então os argumentos de palavra-chave serão empacotados em um hash e atribuídos ao último parâmetro.
>> def my_method(a)= a
>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}
*arguments e **keyword_arguments também podem ser usados em combinação um com o outro:
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"
Você também pode escrever argumentos antes e depois de *arguments para permitir argumentos posicionais específicos.
Isso funciona do mesmo jeito que decompor um array.
Os argumentos precisam estar estruturados em uma ordem específica:
def my_method(<positional_arguments>, *arguments, <positional_arguments>, <key-word_arguments>, **keyword_arguments)
Se você não seguir essa ordem, vai receber um erro.
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]
Você pode escrever argumentos posicionais antes e depois de *arguments:
>> def my_method(a, *middle, b)= middle
>> my_method(1, 2, 3, 4, 5)
=> [2, 3, 4]
Você também pode combinar argumentos posicionais, *arguments, argumentos de palavra-chave 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}
Escrever os argumentos em uma ordem incorreta vai resultar em um erro:
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)
Você pode usar o operador splat (*) para desempacotar um array de argumentos em uma chamada de 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
Você também pode usar o operador splat duplo (**) para desempacotar um hash de argumentos em uma chamada de 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