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

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

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


当前回答

如下所示:

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

其他回答

tags.map(&:name)

是一样的

tags.map{|tag| tag.name}

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

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

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

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

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)

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它所期望的块。

另一个很酷的简写,很多人不知道,是

array.each(&method(:foo))

这是一个简写

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

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

这是非常有用的,当你想做点自由风格的事情。一个例子是检查数组中是否有字符串等于字符串“foo”。有一种传统的方式:

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

还有一种简单的方法:

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

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

这里:name是指向标记对象的方法名的符号。 当我们将&:name传递给map时,它将把name作为一个proc对象。 简而言之,tags.map(&:name)充当:

tags.map do |tag|
  tag.name
end

如下所示:

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