map(&:name)在Ruby中意味着什么?

我在RailsCast中找到了这段代码:

def tag_names @tag_names || tags.map(&:name).join(' ') end 

map(&:name)是什么意思?

它是tags.map(&:name.to_proc).join(' ')的简写

如果foo是一个带有to_proc方法的对象,那么你可以将它传递给一个方法作为&foo ,它将调用foo.to_proc并将其用作方法的块。

Symbol#to_proc方法最初由ActiveSupport添加,但已集成到Ruby 1.8.7中。 这是它的实现:

 class Symbol def to_proc Proc.new do |obj, *args| obj.send self, *args end end end 

许多人都不知道另一个很酷的速记

 array.each(&method(:foo)) 

这是一个简写

 array.each { |element| foo(element) } 

通过调用method(:foo)我们从self了一个表示其foo方法的Method对象,并使用&表示它有一个to_proc 方法 ,将其转换为Proc

当你想做无点风格的事情时,这非常有用。 一个例子是检查数组中是否有任何字符串等于字符串"foo" 。 有传统方式:

 ["bar", "baz", "foo"].any? { |str| str == "foo" } 

并且有无点的方式:

 ["bar", "baz", "foo"].any?(&"foo".method(:==)) 

首选方式应该是最易读的方法。

它相当于

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

我们还要注意,&符号#to_proc魔法可以与任何类一起使用,而不仅仅是符号。 许多#to_proc选择在Array类上定义#to_proc

 class Array def to_proc proc { |receiver| receiver.send *self } end end # And then... [ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ] #=> ["Hello world!", "Goodbye world!"] 

Ampersand &通过在其操作数上发送to_proc消息来工作,在上面的代码中,该操作数是Array类。 由于我在Array上定义了#to_proc方法,因此该行变为:

 [ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) } 

它是tags.map { |tag| tag.name }.join(' ')的缩写 tags.map { |tag| tag.name }.join(' ')

 tags.map(&:name) 

是相同的

 tags.map{|tag| tag.name} 

&:name只使用符号作为要调用的方法名称。

Josh Lee的答案几乎是正确的,除了等效的Ruby代码应该如下。

 class Symbol def to_proc Proc.new do |receiver| receiver.send self end end end 

 class Symbol def to_proc Proc.new do |obj, *args| obj.send self, *args end end end 

使用此代码,当执行print [[1,'a'],[2,'b'],[3,'c']].map(&:first) ,Ruby会分割第一个输入[1,'a']到1和’a’给obj 1和args* ‘a’引起错误,因为Fixnum对象1没有方法self(这是:first)。


[[1,'a'],[2,'b'],[3,'c']].map(&:first)被执行时;

  1. :first是一个Symbol对象,所以当&:first作为参数赋给map方法时,会调用Symbol#to_proc。

  2. map将调用消息发送到:first.to_proc,参数为[1,'a'] ,例如:first.to_proc.call([1,'a'])被执行。

  3. Symbol类中的to_proc过程使用参数(:first)向数组对象( [1,'a'] )发送发送消息,例如, [1,'a'].send(:first)执行[1,'a'].send(:first)

  4. 迭代[[1,'a'],[2,'b'],[3,'c']]对象中的其余元素。

这与执行[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)表达式相同。

这里发生了两件事情,理解两者都很重要。

如其他答案中所述,正在调用Symbol#to_proc方法。

但是在符号上调用to_proc的原因是因为它被作为块参数传递给map 。 在方法调用中放置&在参数前面会导致它以这种方式传递。 这适用于任何Ruby方法,而不仅仅是符号map

 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: # some_method(&"whatever") # TypeError: wrong argument type String (expected Proc) # (String doesn't respond to #to_proc) 

Symbol转换为Proc因为它作为块传入。 我们可以通过尝试将proc传递给没有&符号的.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"] 

即使它不需要转换,该方法也不会知道如何使用它,因为它需要一个块参数。 传递它并给.map它预期的块。

(&:name)是(&:name.to_proc)的缩写,与tags.map{ |t| t.name }.join(' ') tags.map{ |t| t.name }.join(' ')

to_proc实际上是用C实现的

虽然我们已经有了很好的答案,但从初学者的角度来看,我想添加其他信息:

map(&:name)在Ruby中意味着什么?

这意味着,您将另一个方法作为参数传递给map函数。 (实际上你传递的是一个符号,它被转换成一个过程。但在这个特殊情况下,这并不重要)。

重要的是你有一个名为namemethod ,它将被map方法用作参数而不是传统的block样式。

这里:name是指向标记对象的方法name的符号。 当我们将&:name传递给map ,它会将name视为proc对象。 简而言之, tags.map(&:name)作用如下:

 tags.map do |tag| tag.name end 

它的意思是

 array.each(&:to_sym.to_proc) 

map(&:name)接受一个可枚举对象(在您的情况下为标记)并为每个元素/标记运行name方法,从方法输出每个返回的值。

这是一个简写

 array.map { |element| element.name } 

返回元素(标记)名称的数组

它如下:

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