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_procmétodo, você pode passá-lo para um método como &foo, que o chamará foo.to_proce usará como bloco do método.
O Symbol#to_procmé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 Methodobjeto selfque representa seu foométodo e usamos o ¶ 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_procmagia e comercial pode funcionar com qualquer classe, não apenas com o Symbol. Muitos Rubyists optam por definir #to_procna 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_procmensagem em seu operando, que, no código acima, é da classe Array. E desde que eu defini o #to_procmé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_procseu 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 obj1 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_procentrega-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_proco símbolo está sendo chamado é porque ele está sendo passado mapcomo 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 mapcom 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 Procporque é passado como um bloco. Podemos mostrar isso tentando passar um proc .mapsem 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 .mapo bloco que ele espera.
Basicamente, executa a chamada de método tag.nameem 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 methodnome nameque será usado pelo método do mapa como argumento em vez do blockestilo tradicional .
Primeiro, &:nameé um atalho para &:name.to_proc, onde :name.to_procretorna a Proc(algo semelhante, mas não idêntico a um lambda) que, quando chamado com um objeto como argumento (primeiro), chama o namemétodo nesse objeto.
Segundo, enquanto &in def foo(&block) ... endconverte um bloco passado para fooa 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 namemé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