O Ruby tem um operador pouco usado por iniciantes, mas que ajuda bastante a trabalhar com parâmetros de um método: o single splat operator.
O Básico
O Single Splat tem muitos casos de uso e a ideia principal é que você não precisa especificar o número de argumentos que você vai ter. Vamos ver um pequeno exemplo:
def hello_names(*names)
case names.count
when 0
puts "Cadê os nomes?"
when 1
puts "Só um nome?"
puts names
else
puts "Agora sim! Muitos nomes:"
puts names
end
end
O método hello_names
recebe o argumento names
. O *
é o single splat, que converte em um array todos os argumentos passados a ele. Neste exemplo, o método poderia ser usado da seguinte forma:
hello_names
# "Cadê os nomes?"
# => nil
hello_names("Fulano")
# Só um nome?
# Fulano
# => nil
hello_names("Fulano", "Sicrano", "Beltrano")
# Agora sim! Muitos nomes:
# Fulano
# Sicrano
# Beltrano
# => nil
Como vocês podem ver pelo primeiro exemplo, isso torna o parâmetro não obrigatório e podemos receber qualquer quantidade. Note também que, no terceiro exemplo, apesar de cada nome ter sido impresso em tela separadamente, a mensagem "Agora sim! Muitos nomes:" foi impressa apenas uma vez, ou seja, não há uma execução do método para cada argumento.
Casos de uso
Quebrando argumentos
Uma opção interessante é que, além de poder ser usado na definição do método, o splat operator também pode ser usado ao chamar um método. Eles permitem passar um array para um método que espera múltiplos argumentos, colocando os argumentos nas posições corretas.
def position(x, y, z)
puts "X: #{x}"
puts "Y: #{y}"
puts "Z: #{z}"
end
position_array = [10, 0, 20]
position(*position_array)
# X: 10
# Y: 0
# Z: 10
# => nil
Assim destrinchamos o array em argumentos posicionais.
Argumentos restantes
Outra opção é receber uma parte dos argumentos e usar o splat operator para capturar o restante.
def full_name(first_name, *middle_names, last_name)
puts "Esse é o nome principal: #{first_name}"
puts "Nomes do meio: #{middle_names}"
puts "Último nome: #{last_name}"
end
full_name("Fulano", "da Silva", "Sauro", "Sicrano")
# Esse é o nome principal: Fulano
# Nomes do meio: ["da Silva", "Sauro"]
# Último nome: Sicrano
# => nil
Head and Tail
Um exemplo ocasional que pode acontecer é você querer desestruturar um array:
head, *tail = [1, 2, 3, 4, 5]
head
# => 1
tail
# => [2, 3, 4, 5]
# ou o contrario
*head, tail = [1, 2, 3, 4, 5]
head
# => [1, 2, 3, 4]
tail
# => 5
# ou até
head, *, last = [1, 2, 3, 4, 5]
head
# => 1
last
# => 5
Construção de arrays
Esse operador pode ser usado também para construir um array e funcionando de forma semelhante ao método flatten
.
first_name = "Fulano"
other_names = ["da Silva", "Sauro"]
[first_name, *other_names]
# => ["Fulano", "da Silva", "Sauro"]
Limitações
Métodos com múltiplos splats
Métodos com múltiplos splats não são válidos devido ao jeito que o Ruby os usa para capturar o início ou final dos argumentos.
def sum(*numbers, *other_numbers)
end
# Traceback (most recent call last):
# 3: from /home/.rvm/rubies/ruby-2.7.1/bin/irb:23:in `<main>'
# 2: from /home/.rvm/rubies/ruby-2.7.1/bin/irb:23:in `load'
# 1: from /home/.rvm/rubies/ruby-2.7.1/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
# SyntaxError ((irb):31: syntax error, unexpected *)
# def sum(*numbers, *other_numbers)
Splat e Keyword Argument
Uma limitação do Ruby é o uso de single splat com keyword arguments. Temos que passar os argumentos keyword por último nas chamadas e na definição:
# tudo certo
def nomes(*nomes, important:)
puts nomes
puts "Nome importante: #{important}"
end
nomes('Claudia', 'João', important: 'Fulano')
# Claudia
# João
# Nome importante: Fulano
# => nil
# Erro
nomes(important: 'Fulano', 'Claudia', 'João')
# irb(main):007:0> nomes(important: 'Fulano', 'Claudia', 'João')
# Traceback (most recent call last):
# 3: from /usr/local/bin/irb:23:in `<main>'
# 2: from /usr/local/bin/irb:23:in `load'
# 1: from /usr/local/lib/ruby/gems/2.7.1/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
# SyntaxError ((irb):7: syntax error, unexpected ',', expecting =>)
# ...important: 'Fulano', 'Claudia', 'João')
# ... ^
# (irb):7: syntax error, unexpected ')', expecting end-of-input
# ...: 'Fulano', 'Claudia', 'João')
# …
Conclusão
É isso! Esse operador é bem legal para simplificar initializers
de classes e outros aspectos de código.
Em breve vamos ver um pouco sobre outro operador com uma função parecida o Double Splat(**)