Ich habe diesen Code in einem RailsCast gefunden :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Was bedeutet das (&:name)
in map(&:name)
?
Ich habe diesen Code in einem RailsCast gefunden :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Was bedeutet das (&:name)
in map(&:name)
?
Antworten:
Es ist eine Abkürzung für tags.map(&:name.to_proc).join(' ')
Wenn foo
es sich um ein Objekt mit einer to_proc
Methode handelt, können Sie es an eine Methode als übergeben &foo
, die diese aufruft foo.to_proc
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 Umsetzung:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
&
, dhtags.map(&:name.to_proc).join(' ')
Eine andere coole Abkürzung, die vielen unbekannt ist, ist
array.each(&method(:foo))
Das ist eine Abkürzung für
array.each { |element| foo(element) }
Durch Aufrufen haben method(:foo)
wir ein Method
Objekt genommen self
, das seine foo
Methode darstellt, und das verwendet, &
um anzuzeigen, dass es eine to_proc
Methode hat , die es in eine konvertiert Proc
.
Dies ist sehr nützlich, wenn Sie einen punktfreien Stil ausführen möchten . Ein Beispiel ist die Überprüfung, ob ein Array eine Zeichenfolge enthält, die der Zeichenfolge entspricht "foo"
. Es gibt den herkömmlichen Weg:
["bar", "baz", "foo"].any? { |str| str == "foo" }
Und da ist der punktfreie Weg:
["bar", "baz", "foo"].any?(&"foo".method(:==))
Der bevorzugte Weg sollte der am besten lesbare sein.
array.each{|e| foo(e)}
ist noch kürzer :-) +1 sowieso
&method
?
[1,2,3].map(&Array.method(:new))
Es ist gleichbedeutend mit
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Beachten wir auch, dass kaufmännisches Und-Zeichen- #to_proc
Magie mit jeder Klasse funktionieren kann, nicht nur mit Symbol. Viele Rubyisten definieren die #to_proc
Array-Klasse wie folgt :
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Das kaufmännische Und &
sendet eine to_proc
Nachricht an seinen Operanden, der im obigen Code zur Array-Klasse gehört. Und da ich die #to_proc
Methode für das Array definiert habe , wird die Zeile wie folgt:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
Es ist eine Abkürzung für tags.map { |tag| tag.name }.join(' ')
&
ruft der unäre Operator to_proc
seinen Operanden auf. Es ist also nicht spezifisch für die Map-Methode und funktioniert tatsächlich mit jeder Methode, die einen Block nimmt und ein oder mehrere Argumente an den Block übergibt.
tags.map(&:name)
ist das gleiche wie
tags.map{|tag| tag.name}
&:name
verwendet nur das Symbol als den aufzurufenden Methodennamen.
Josh Lees Antwort ist fast richtig, außer dass der entsprechende Ruby-Code wie folgt lauten 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
Wenn dieser Code print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
ausgeführt wird, teilt Ruby die erste Eingabe [1,'a']
in 1 und 'a' auf, um obj
1 zu geben, und args*
'a', um einen Fehler zu verursachen, da das Fixnum-Objekt 1 nicht über die Methode self verfügt (dh: first).
Wann [[1,'a'],[2,'b'],[3,'c']].map(&:first)
wird ausgeführt;
:first
ist ein Symbol-Objekt. Wenn &:first
also eine Map-Methode als Parameter angegeben wird, wird Symbol # to_proc aufgerufen.
map sendet eine Aufrufnachricht an: first.to_proc mit Parameter [1,'a']
, zB :first.to_proc.call([1,'a'])
wird ausgeführt.
Die Prozedur to_proc in der Symbolklasse sendet eine Sende-Nachricht an ein Array-Objekt ( [1,'a']
) mit dem Parameter (: first), z [1,'a'].send(:first)
. B. wird ausgeführt.
iteriert über den Rest der Elemente im [[1,'a'],[2,'b'],[3,'c']]
Objekt.
Dies ist dasselbe wie das Ausführen eines [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
Ausdrucks.
[1,2,3,4,5,6].inject(&:+)
, dass Inject ein Lambda mit zwei Parametern (Memo und Item) erwartet und es :+.to_proc
liefert - Proc.new |obj, *args| { obj.send(self, *args) }
oder{ |m, o| m.+(o) }
Hier passieren zwei Dinge, und es ist wichtig, beide zu verstehen.
Wie in anderen Antworten beschrieben, wird die Symbol#to_proc
Methode aufgerufen.
Der Grund to_proc
, warum das Symbol aufgerufen wird, ist, dass es map
als Blockargument übergeben wird. Wenn Sie &
in einem Methodenaufruf vor einem Argument stehen, wird es auf diese Weise übergeben. Dies gilt für jede Ruby-Methode, nicht nur map
für Symbole.
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 a konvertiert, Proc
weil es als Block übergeben wird. Wir können dies zeigen, indem wir versuchen, einen Proc .map
ohne kaufmännisches Und weiterzugeben :
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 es verwendet werden soll, da sie ein Blockargument erwartet. Passen Sie es mit &
gibt .map
den Block , es erwartet.
map (&: name) verwendet ein aufzählbares Objekt (in Ihrem Fall Tags) und führt die Namensmethode für jedes Element / Tag aus, wobei jeder zurückgegebene Wert von der Methode ausgegeben wird.
Es ist eine Abkürzung für
array.map { |element| element.name }
Dies gibt das Array der Elementnamen (Tag-Namen) zurück
Grundsätzlich wird der Methodenaufruf tag.name
für jedes Tag im Array ausgeführt.
Es ist eine vereinfachte Rubin-Abkürzung.
Obwohl wir bereits gute Antworten haben, möchte ich aus der Perspektive eines Anfängers die zusätzlichen Informationen hinzufügen:
Was bedeutet map (&: name) in Ruby?
Dies bedeutet, dass Sie eine andere Methode als Parameter an die Kartenfunktion übergeben. (In Wirklichkeit übergeben Sie ein Symbol, das in einen Proc umgewandelt wird. Dies ist jedoch in diesem speziellen Fall nicht so wichtig.)
Wichtig ist, dass Sie einen method
Namen haben name
, der von der Map-Methode als Argument anstelle des traditionellen block
Stils verwendet wird.
Zuerst, &:name
ist dies eine Verknüpfung für &:name.to_proc
, bei :name.to_proc
der ein Proc
(etwas, das einem Lambda ähnlich, aber nicht identisch ist) zurückgegeben wird, das beim Aufrufen mit einem Objekt als (erstem) Argument die name
Methode für dieses Objekt aufruft .
Zweitens während &
in def foo(&block) ... end
wandelt ein Block zu übergeben , foo
um einen Proc
, sie tut das Gegenteil , wenn sie einen aufgebracht Proc
.
Es handelt sich also &:name.to_proc
um einen Block, der ein Objekt als Argument verwendet und die name
Methode dafür aufruft , d { |o| o.name }
. H.
Es ist das gleiche wie unten:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end