Encontrei esse código em um RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
O que significa (&:name)
in map(&:name)
?
Encontrei esse código em um RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
O que significa (&:name)
in map(&:name)
?
Respostas:
É uma abreviação de tags.map(&:name.to_proc).join(' ')
Se foo
é um objeto com um to_proc
método, você pode passá-lo para um método como &foo
, que o chamará foo.to_proc
e usará como bloco do método.
O Symbol#to_proc
método foi originalmente adicionado pelo ActiveSupport, mas foi integrado ao Ruby 1.8.7. Esta é a sua implementação:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
&
, ou sejatags.map(&:name.to_proc).join(' ')
Outra abreviação legal, desconhecida para muitos, é
array.each(&method(:foo))
que é uma abreviação de
array.each { |element| foo(element) }
Ao chamar method(:foo)
, pegamos um Method
objeto self
que representa seu foo
método e usamos o &
para significar que ele possui um to_proc
método que o converte em a Proc
.
Isso é muito útil quando você deseja fazer coisas sem estilo. Um exemplo é verificar se há alguma string em uma matriz igual à string "foo"
. Existe a maneira convencional:
["bar", "baz", "foo"].any? { |str| str == "foo" }
E existe o caminho sem pontos:
["bar", "baz", "foo"].any?(&"foo".method(:==))
A maneira preferida deve ser a mais legível.
array.each{|e| foo(e)}
é mais curto ainda :-) +1 de qualquer maneira
&method
?
[1,2,3].map(&Array.method(:new))
É equivalente a
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Embora também observemos que a #to_proc
magia e comercial pode funcionar com qualquer classe, não apenas com o Symbol. Muitos Rubyists optam por definir #to_proc
na classe Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
E comercial &
funciona enviando to_proc
mensagem em seu operando, que, no código acima, é da classe Array. E desde que eu defini o #to_proc
método na matriz, a linha se torna:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
É uma abreviação de tags.map { |tag| tag.name }.join(' ')
&
operador unário chama to_proc
seu operando. Portanto, não é específico para o método de mapa e, de fato, funciona em qualquer método que pega um bloco e passa um ou mais argumentos para o bloco.
A resposta de Josh Lee está quase correta, exceto que o código Ruby equivalente deveria ter sido o seguinte.
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
não
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Com esse código, quando print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
é executado, Ruby divide a primeira entrada [1,'a']
em 1 e 'a' para fornecer obj
1 e args*
'a' para causar um erro, pois o objeto Fixnum 1 não possui o método self (que é: first).
Quando [[1,'a'],[2,'b'],[3,'c']].map(&:first)
é executado;
:first
é um objeto Symbol, portanto, quando &:first
é fornecido um método de mapa como parâmetro, o símbolo # to_proc é chamado.
O mapa envia uma mensagem de chamada para: first.to_proc com o parâmetro [1,'a']
, por exemplo, :first.to_proc.call([1,'a'])
é executado.
O procedimento to_proc na classe Symbol envia uma mensagem de envio para um objeto de matriz ( [1,'a']
) com o parâmetro (: first), por exemplo, [1,'a'].send(:first)
é executado.
itera sobre o restante dos elementos no [[1,'a'],[2,'b'],[3,'c']]
objeto.
É o mesmo que executar [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
expressão.
[1,2,3,4,5,6].inject(&:+)
- injetar espera um lambda com dois parâmetros (MEMO e item) e :+.to_proc
entrega-lo - Proc.new |obj, *args| { obj.send(self, *args) }
ou{ |m, o| m.+(o) }
Duas coisas estão acontecendo aqui, e é importante entender as duas coisas.
Conforme descrito em outras respostas, o Symbol#to_proc
método está sendo chamado.
Mas o motivo pelo qual to_proc
o símbolo está sendo chamado é porque ele está sendo passado map
como um argumento de bloco. Colocar &
na frente de um argumento em uma chamada de método faz com que seja passado dessa maneira. Isso é verdade para qualquer método Ruby, não apenas map
com símbolos.
def some_method(*args, &block)
puts "args: #{args.inspect}"
puts "block: #{block.inspect}"
end
some_method(:whatever)
# args: [:whatever]
# block: nil
some_method(&:whatever)
# args: []
# block: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)
O Symbol
é convertido em um Proc
porque é passado como um bloco. Podemos mostrar isso tentando passar um proc .map
sem o e comercial:
arr = %w(apple banana)
reverse_upcase = proc { |i| i.reverse.upcase }
reverse_upcase.is_a?(Proc)
=> true
arr.map(reverse_upcase)
# ArgumentError: wrong number of arguments (1 for 0)
# (map expects 0 positional arguments and one block argument)
arr.map(&reverse_upcase)
=> ["ELPPA", "ANANAB"]
Mesmo que não precise ser convertido, o método não saberá como usá-lo, pois espera um argumento de bloco. Passá-lo &
fornece .map
o bloco que ele espera.
Basicamente, executa a chamada de método tag.name
em cada tag na matriz.
É uma abreviação simplificada de rubi.
Embora já tenhamos ótimas respostas, procurando uma perspectiva de iniciante, gostaria de adicionar as informações adicionais:
O que significa map (&: name) em Ruby?
Isso significa que você está passando outro método como parâmetro para a função de mapa. (Na realidade, você está passando um símbolo que é convertido em um processo. Mas isso não é tão importante nesse caso específico).
O importante é que você tenha um method
nome name
que será usado pelo método do mapa como argumento em vez do block
estilo tradicional .
Primeiro, &:name
é um atalho para &:name.to_proc
, onde :name.to_proc
retorna a Proc
(algo semelhante, mas não idêntico a um lambda) que, quando chamado com um objeto como argumento (primeiro), chama o name
método nesse objeto.
Segundo, enquanto &
in def foo(&block) ... end
converte um bloco passado para foo
a Proc
, ele faz o oposto quando aplicado a a Proc
.
Assim, &:name.to_proc
é um bloco que pega um objeto como argumento e chama o name
método nele, ie { |o| o.name }
.
É o mesmo que abaixo:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end