Ich habe diesen Code in a RailsCast gefunden:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Was bedeutet der (&:name)
in map(&:name)
?
Es ist die Abkürzung für tags.map(&:name.to_proc).join(' ')
Wenn foo
ein Objekt mit einer to_proc
-Methode ist, können Sie es als &foo
an eine Methode übergeben, die foo.to_proc
aufruft und als Block der Methode verwendet.
Die Symbol#to_proc
-Methode wurde ursprünglich von ActiveSupport hinzugefügt, wurde jedoch in Ruby 1.8.7 integriert. Dies ist seine Implementierung:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Eine andere, für viele unbekannte Abkürzung ist
array.each(&method(:foo))
was ist eine Abkürzung für
array.each { |element| foo(element) }
Durch den Aufruf von method(:foo)
haben wir ein Method
-Objekt von self
genommen, das seine foo
-Methode darstellt, und mit &
angegeben, dass es einen to_proc
method hat, das es in eine Proc
umwandelt.
Dies ist sehr nützlich, wenn Sie den Punkt-freien Stil ausführen möchten. Ein Beispiel ist die Überprüfung, ob sich in einem Array ein String befindet, der dem String "foo"
entspricht. Es gibt den herkömmlichen Weg:
["bar", "baz", "foo"].any? { |str| str == "foo" }
Und es gibt den punktlosen Weg:
["bar", "baz", "foo"].any?(&"foo".method(:==))
Der bevorzugte Weg sollte der lesbarste sein.
Es ist gleichbedeutend mit
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Beachten Sie, dass das kaufmännische Und #to_proc
magie mit jeder Klasse und nicht nur mit Symbol funktionieren kann. Viele Rubyisten definieren #to_proc
für die Array-Klasse:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Ampersand &
sendet eine to_proc
-Nachricht an seinen Operanden, der im obigen Code der Array-Klasse entspricht. Und da ich die #to_proc
-Methode für Array definiert habe, wird die Zeile wie folgt:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
Es ist die Abkürzung für tags.map { |tag| tag.name }.join(' ')
tags.map(&:name)
ist das gleiche wie
tags.map{|tag| tag.name}
&:name
verwendet das Symbol nur als aufzurufenden Methodennamen.
Josh Lees Antwort ist fast richtig, mit der Ausnahme, dass der entsprechende Ruby-Code wie folgt aussehen sollte.
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
nicht
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Bei diesem Code teilt Ruby bei Ausführung von print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
die erste Eingabe [1,'a']
in 1 und 'a', um obj
1 und args*
'a' zu geben, um einen Fehler zu verursachen, da Fixnum-Objekt 1 nicht die Methode self hat (was zuerst ist). .
Wenn [[1,'a'],[2,'b'],[3,'c']].map(&:first)
ausgeführt wird;
:first
ist ein Symbol-Objekt. Wenn &:first
einer Map-Methode als Parameter übergeben wird, wird Symbol # to_proc aufgerufen.
map sendet eine Anrufnachricht an: first.to_proc mit dem Parameter [1,'a']
, z. B. :first.to_proc.call([1,'a'])
wird ausgeführt.
die to_proc-Prozedur in der Symbol-Klasse sendet eine Sende-Nachricht mit einem Parameter (: first) an ein Array-Objekt ([1,'a']
), z. B. wird [1,'a'].send(:first)
ausgeführt.
iteriert über den Rest der Elemente in [[1,'a'],[2,'b'],[3,'c']]
-Objekt.
Dies ist das gleiche wie das Ausführen von [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
.
Zwei Dinge passieren hier und es ist wichtig, beide zu verstehen.
Wie in anderen Antworten beschrieben, wird die Symbol#to_proc
-Methode aufgerufen.
Der Grund, warum to_proc
für das Symbol aufgerufen wird, liegt darin, dass es als Blockargument an map
übergeben wird. Wenn Sie &
vor einem Argument in einem Methodenaufruf platzieren, wird es auf diese Weise übergeben. Dies gilt für jede Ruby-Methode, nicht nur für map
mit Symbolen.
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)
Das Symbol
wird in ein Proc
konvertiert, da es als Block übergeben wird. Wir können dies zeigen, indem wir versuchen, ein proc ohne das kaufmännische Und an .map
zu übergeben:
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"]
Obwohl es nicht konvertiert werden muss, weiß die Methode nicht, wie sie verwendet werden soll, da sie ein Blockargument erwartet. Bei der Übergabe mit &
erhält .map
den erwarteten Block.
(&: name) ist eine Abkürzung für (&: name.to_proc) und ist mit tags.map{ |t| t.name }.join(' ')
identisch.
to_proc ist tatsächlich in C implementiert
Obwohl wir bereits großartige Antworten haben, möchte ich die zusätzlichen Informationen hinzufügen:
Was bedeutet map (&: name) in Ruby?
Das bedeutet, dass Sie eine andere Methode als Parameter an die Kartenfunktion übergeben. (In Wirklichkeit übergeben Sie ein Symbol, das in eine Prozedur umgewandelt wird. Dies ist jedoch in diesem speziellen Fall nicht so wichtig).
Wichtig ist, dass Sie eine method
mit dem Namen name
haben, die von der map-Methode als Argument anstelle des traditionellen block
-Stils verwendet wird.
map (&: name) nimmt ein zahlbares Objekt (Tags in Ihrem Fall) und führt die Name-Methode für jedes Element/Tag aus, wobei jeder zurückgegebene Wert aus der Methode ausgegeben wird.
Es ist eine Abkürzung für
array.map { |element| element.name }
welcher das Array der Elementnamen (Tag-Namen) zurückgibt
Hier ist :name
das Symbol, das auf die Methode name
des Tag-Objekts verweist. Wenn wir &:name
an map
übergeben, wird name
als proc-Objekt behandelt. Kurz gesagt, tags.map(&:name)
wirkt wie:
tags.map do |tag|
tag.name
end
es bedeutet
array.each(&:to_sym.to_proc)
Es ist das gleiche wie unten:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end
Es führt grundsätzlich den Methodenaufruf tag.name
für alle Tags im Array aus.
Es ist eine vereinfachte Ruby-Abkürzung.