wake-up-neo.com

Was bedeutet map (&: name) in Ruby?

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)?

470
collimarco

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
489
Josh Lee

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_procmethod 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.

163
Gerry

Es ist gleichbedeutend mit

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end
72
Sophie Alpert

Beachten Sie, dass das kaufmännische Und #to_procmagie 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!' ) }
41
Boris Stitnicky

Es ist die Abkürzung für tags.map { |tag| tag.name }.join(' ')

36
Oliver N.
tags.map(&:name)

ist das gleiche wie

tags.map{|tag| tag.name}

&:name verwendet das Symbol nur als aufzurufenden Methodennamen.

29
Albert.Qing

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;

  1. :first ist ein Symbol-Objekt. Wenn &:first einer Map-Methode als Parameter übergeben wird, wird Symbol # to_proc aufgerufen.

  2. map sendet eine Anrufnachricht an: first.to_proc mit dem Parameter [1,'a'], z. B. :first.to_proc.call([1,'a']) wird ausgeführt. 

  3. 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.

  4. 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).

14
prosseek

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.

10
devpuppy

(&: 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

5
tessie

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.

2
Jonathan Duarte

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

1
Sunda

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
1
timlentse

es bedeutet

array.each(&:to_sym.to_proc)
1
mminski

Es ist das gleiche wie unten:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
0
user3078630

Es führt grundsätzlich den Methodenaufruf tag.name für alle Tags im Array aus.

Es ist eine vereinfachte Ruby-Abkürzung.

0