Single Splat Operator

Artigos - 16/Set/2020 - por Henrique Morato

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(**)

Referências

Foto de perfil do autor
Henrique Morato

Dev