536

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

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

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

4

16 回答 16

535

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

如果foo是带有to_proc方法的对象,那么您可以将其传递给方法 as &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
于 2009-08-01T17:50:37.423 回答
190

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

array.each(&method(:foo))

这是一个简写

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

通过调用method(:foo),我们Methodself代表其foo方法的对象中获取一个对象,并使用&来表示它有一个to_proc 其转换为Proc.

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

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

还有一种免点方式:

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

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

于 2012-03-08T18:07:09.230 回答
80

相当于

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end
于 2009-08-01T17:39:52.267 回答
59
tags.map(&:name)

是相同的

tags.map{|tag| tag.name}

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

于 2016-11-01T03:23:29.983 回答
50

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

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 类。由于我#to_proc在 Array 上定义了方法,因此该行变为:

[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
于 2012-11-20T12:38:28.233 回答
41

这是简写tags.map { |tag| tag.name }.join(' ')

于 2009-08-01T17:37:34.033 回答
16

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

如其他答案中所述,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: #<Proc:0x007fd23d010da8>

some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)

Symbol转换为 aProc因为它是作为一个块传入的。我们可以通过尝试在.map没有 & 符号的情况下传递 proc 来证明这一点:

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

于 2016-04-09T01:43:55.403 回答
14

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' 以给出obj1 和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. [1,'a']Symbol 类中的 to_proc 过程向带有参数 (:first)的数组对象 ( ) 发送发送消息,例如,[1,'a'].send(:first)被执行。

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

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

于 2014-01-23T21:08:40.823 回答
5

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

to_proc 实际上是用 C 实现的

于 2016-08-25T05:50:15.210 回答
4

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

这是一个简写

array.map { |element| element.name }

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

于 2018-09-01T04:48:06.470 回答
4

首先,&:name是 的快捷方式&:name.to_proc,其中:name.to_proc返回一个Proc(与 lambda 类似但不完全相同的东西),当使用对象作为(第一个)参数调用时,会调用该name对象上的方法。

其次,虽然&indef foo(&block) ... end将传递给的块转换foo为 a Proc,但在应用于 a 时却相反Proc

因此,&:name.to_proc是一个将对象作为参数并调用其name方法的块,即{ |o| o.name }.

于 2020-03-15T22:22:59.610 回答
3

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

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

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

重要的是您有一个method名称,该名称name将被 map 方法用作参数而不是传统block样式。

于 2017-12-08T15:23:39.813 回答
2

它基本上对数组中的每个标签执行方法调用tag.name

它是一种简化的 ruby​​ 速记。

于 2019-02-12T16:15:36.780 回答
1

:name是指向name标签对象方法的符号。当我们传递&:namemap时,它将被name视为 proc 对象。简而言之,tags.map(&:name)充当:

tags.map do |tag|
  tag.name
end
于 2016-06-30T06:30:44.097 回答
1

它的意思是

array.each(&:to_sym.to_proc)
于 2016-12-20T12:25:09.773 回答
-1

它与以下相同:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
于 2016-06-27T09:32:18.873 回答