Was ist Karte (&: Name) bedeuten in Ruby?
-
07-07-2019 - |
Frage
Ich habe diesen Code in ein Railscast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Was bedeutet der (&:name)
in map(&:name)
bedeuten?
Lösung
Es ist eine Abkürzung für tags.map(&:name.to_proc).join(' ')
Wenn foo
ein Objekt mit einem to_proc
Methode ist, dann kann man es sich auf ein Verfahren, wie &foo
passieren, die foo.to_proc
nennen und verwenden, die als die Blockmethode.
Die Symbol#to_proc
Methode wurde ursprünglich von Active hinzugefügt wurde aber 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
Andere Tipps
Eine andere kühle Kurzschrift, vielen unbekannt ist
array.each(&method(:foo))
das ist eine Abkürzung für
array.each { |element| foo(element) }
Mit dem method(:foo)
Aufruf wir nahmen ein Method
Objekt aus self
, das seiner foo
Methode darstellt, und verwenden, um den &
um anzuzeigen, dass es eine to_proc
hat Methode , die es in einem Proc
umwandelt.
Das ist sehr nützlich, wenn man die Dinge Punkt frei Stil tun will. Ein Beispiel ist zu überprüfen, ob es eine Zeichenfolge in einem Array ist, das auf den String "foo"
gleich ist. Es ist die herkömmliche Art und Weise:
["bar", "baz", "foo"].any? { |str| str == "foo" }
Und es ist der Punkt, freie Art und Weise:
["bar", "baz", "foo"].any?(&"foo".method(:==))
Die bevorzugte Art und Weise sollte die lesbar sein.
Es ist äquivalent zu
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Während wir uns auch beachten, dass Ampersand #to_proc
Magie mit jeder Klasse arbeiten können, nicht nur Symbol. Viele Rubyisten wählen #to_proc
auf Array-Klasse zu definieren:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Ampersand &
Werke durch das Senden to_proc
Nachricht auf seinem Operanden, die in dem obigen Code, der Array-Klasse ist. Und da ich #to_proc
Methode auf Array definiert, die Linie wird:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
Es ist eine Abkürzung für tags.map { |tag| tag.name }.join(' ')
tags.map(&:name)
ist die gleiche wie
tags.map{|tag| tag.name}
&:name
verwendet nur das Symbol als die Methodennamen aufgerufen werden.
Josh Lee Antwort ist fast richtig, außer dass der äquivalent Ruby-Code sein sollte wie folgt.
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
Mit diesem Code, wenn print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
ausgeführt wird, teilt Ruby den ersten Eingang [1,'a']
in 1 und ‚a‘ obj
1 und args*
‚a‘ zu veranlassen, einen Fehler als Fixnum Objekt geben 1 nicht das Verfahren selbst hat (das ist :zuerst).
Wenn [[1,'a'],[2,'b'],[3,'c']].map(&:first)
ausgeführt wird;
-
:first
ist ein Symbol Objekt, so dass, wenn&:first
auf eine Karte Methode als Parameter angegeben ist, wird Symbol # to_proc aufgerufen. -
Karte Anruf Nachricht an sendet: first.to_proc mit Parametern
[1,'a']
, zum Beispiel:first.to_proc.call([1,'a'])
ausgeführt. -
to_proc Prozedur in Symbolklasse sendet eine Nachricht senden, um ein Array-Objekt (
[1,'a']
) mit dem Parameter (: zuerst). Z.B.[1,'a'].send(:first)
ausgeführt -
iteriert über den Rest der Elemente in
[[1,'a'],[2,'b'],[3,'c']]
Objekt.
Dies ist die gleiche wie [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
Ausdruck ausgeführt wird.
Zwei Dinge geschehen hier, und es ist wichtig, beide zu verstehen.
Wie in anderen Antworten beschrieben, die Symbol#to_proc
Methode aufgerufen wird.
Aber der Grund to_proc
wird auf dem Symbol genannt wird, weil sie als Block Argument map
übergeben werden werden. Platzieren &
vor einem Streit in einem Methodenaufruf bewirkt, dass es auf diese Weise weitergegeben werden. Dies gilt für alle Ruby-Methode, nicht nur 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)
Die Symbol
auf einen Proc
umgewandelt werden, weil es als Block übergeben wird. Wir können dies zeigen, indem sie versuchen, eine proc zu passieren, ohne den Ampersand .map
:
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"]
Auch wenn es nicht konvertiert werden muss, weiß die Methode nicht, wie es zu benutzen, weil es ein Block-Argument erwartet. gibt es mit &
Passing .map
es erwartet den Block.
(&: name) ist die Abkürzung für (&: name.to_proc) es ist die gleiche wie tags.map{ |t| t.name }.join(' ')
to_proc ist in C tatsächlich umgesetzt
Obwohl wir bereits große Antworten haben, auf der Suche durch eine Perspektive eines Anfängers würde Ich mag die zusätzliche Informationen hinzuzufügen:
Was ist Karte (&: Name)? Bedeuten in Ruby
Das bedeutet, dass Sie eine andere Methode als Parameter an die Kartenfunktion sind vorbei. (In Wirklichkeit Sie vorbei ein Symbol, das in ein proc umgewandelt wird. Aber das ist nicht so wichtig, in diesem speziellen Fall).
Was wichtig ist, ist, dass Sie eine method
namens name
haben, die anstelle des traditionellen block
Stil als Argument durch die Kartenmethode verwendet wird.
Karte. (&: Name) nimmt ein zählbares Objekt (Tags in Ihrem Fall) und führt die Namen Methode für jedes Element / Tag, jeden zurückgegebene Wertes von der Methode zur Ausgabe
Es ist eine Abkürzung für
array.map { |element| element.name }
, die das Array von Elementzurückgibt (tag) Namen
Hier :name
ist das Symbol, das auf die Methode name
von Tag-Objekt.
Wenn wir &:name
zu map
passieren, wird es name
als proc Objekt behandeln.
Für kurze, tags.map(&:name)
wirkt wie:
tags.map do |tag|
tag.name
end
bedeutet
array.each(&:to_sym.to_proc)
Es besteht im Wesentlichen den Methodenaufruf tag.name
in der Anordnung auf jeder Tags auszuführen.
Es ist eine vereinfachte rubin Stenografie.
Es ist die gleiche wie unter:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end