我在a RailsCast找到了这段代码:
def tag_names @tag_names || tags.map(&:name).join(' ') end
map(&:name) 中的 (&:name) 是什么意思?
map(&:name)
(&:name)
这是 tags.map(&:name.to_proc).join(' ') 的简写
tags.map(&:name.to_proc).join(' ')
如果 foo 是具有 to_proc 方法的对象,则可以将其作为 &foo 传递给方法,该方法将调用 foo.to_proc 并将其用作方法的块 .
foo
to_proc
&foo
foo.to_proc
Symbol#to_proc 方法最初由ActiveSupport添加,但已集成到Ruby 1.8.7中 . 这是它的实现:
Symbol#to_proc
class Symbol def to_proc Proc.new do |obj, *args| obj.send self, *args end end end
它如下:
def tag_names if @tag_names @tag_names else tags.map{ |t| t.name }.join(' ') end
另一个很酷的简写,很多人都不知道
array.each(&method(:foo))
这是一个简写
array.each { |element| foo(element) }
通过调用 method(:foo) ,我们从 self 中获取了一个表示其 foo 方法的 Method 对象,并使用 & 表示它具有 to_proc method,将其转换为 Proc .
method(:foo)
self
Method
&
Proc
当你想做无点风格的事情时,这非常有用 . 一个示例是检查数组中是否有任何字符串等于字符串 "foo" . 有传统方式:
"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 魔法可以用于任何类,而不仅仅是符号 . 许多Rubyists选择在Array类上定义 #to_proc :
#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)
is The same as
tags.map{|tag| tag.name}
&:name 只使用符号作为要调用的方法名称 .
&:name
Josh Lee的答案几乎是正确的,除了等效的Ruby代码应该如下 .
class Symbol def to_proc Proc.new do |receiver| receiver.send self 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) .
print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
[1,'a']
obj
args*
当 [[1,'a'],[2,'b'],[3,'c']].map(&:first) 执行时;
[[1,'a'],[2,'b'],[3,'c']].map(&:first)
:first 是一个Symbol对象,因此当 &:first 作为参数提供给map方法时,将调用Symbol#to_proc .
:first
&:first
map将调用消息发送到:first.to_proc,其参数为 [1,'a'] ,例如, :first.to_proc.call([1,'a']) 被执行 .
:first.to_proc.call([1,'a'])
Symbol类中的
to_proc过程使用参数(:first)向数组对象( [1,'a'] )发送发送消息,例如执行 [1,'a'].send(:first) .
[1,'a'].send(:first)
遍历 [[1,'a'],[2,'b'],[3,'c']] 对象中的其余元素 .
[[1,'a'],[2,'b'],[3,'c']]
这与执行 [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first) 表达式相同 .
[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
这里发生了两件事情,理解两者都很重要 .
如其他答案中所述,正在调用 Symbol#to_proc 方法 .
但是在符号上调用 to_proc 的原因是因为它作为块参数被传递给 map . 在方法调用中将 & 放在参数前面会导致它以这种方式传递 . 这适用于任何Ruby方法,而不仅仅是符号的 map .
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 被转换为 Proc ,因为它作为块传入 . 我们可以通过尝试将proc传递给没有&符号的 .map 来显示:
Symbol
.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函数 . (实际上你传递的是一个符号,它被转换成一个过程 . 但在这个特殊情况下,这并不重要) .
重要的是你有一个 method 名为 name ,它将被map方法用作参数而不是传统的 block 样式 .
method
name
block
这里 :name 是指向标签对象的方法 name 的符号 . 当我们将 &:name 传递给 map 时,它会将 name 视为proc对象 . 简而言之, tags.map(&:name) 充当:
:name
tags.map do |tag| tag.name end
它的意思是
array.each(&:to_sym.to_proc)
map(&:name) 获取一个可枚举对象(在您的情况下为标记)并为每个元素/标记运行name方法,输出每个返回值从方法 .
array.map { |element| element.name }
返回元素(标记)名称的数组
14 回答
这是
tags.map(&:name.to_proc).join(' ')
的简写如果
foo
是具有to_proc
方法的对象,则可以将其作为&foo
传递给方法,该方法将调用foo.to_proc
并将其用作方法的块 .Symbol#to_proc
方法最初由ActiveSupport添加,但已集成到Ruby 1.8.7中 . 这是它的实现:它如下:
另一个很酷的简写,很多人都不知道
这是一个简写
通过调用
method(:foo)
,我们从self
中获取了一个表示其foo
方法的Method
对象,并使用&
表示它具有to_proc
method,将其转换为Proc
.当你想做无点风格的事情时,这非常有用 . 一个示例是检查数组中是否有任何字符串等于字符串
"foo"
. 有传统方式:并且有无点的方式:
首选方式应该是最易读的方法 .
它相当于
虽然让我们也注意到&符号
#to_proc
魔法可以用于任何类,而不仅仅是符号 . 许多Rubyists选择在Array类上定义#to_proc
:Ampersand
&
通过在其操作数上发送to_proc
消息来工作,在上面的代码中,该操作数是Array类 . 由于我在Array上定义了#to_proc
方法,因此该行变为:这是
tags.map { |tag| tag.name }.join(' ')
的简写is The same as
&:name
只使用符号作为要调用的方法名称 .Josh Lee的答案几乎是正确的,除了等效的Ruby代码应该如下 .
不
使用此代码,当执行
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)
执行时;:first
是一个Symbol对象,因此当&:first
作为参数提供给map方法时,将调用Symbol#to_proc .map将调用消息发送到:first.to_proc,其参数为
[1,'a']
,例如,:first.to_proc.call([1,'a'])
被执行 .Symbol类中的
to_proc过程使用参数(:first)向数组对象(
[1,'a']
)发送发送消息,例如执行[1,'a'].send(:first)
.遍历
[[1,'a'],[2,'b'],[3,'c']]
对象中的其余元素 .这与执行
[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
表达式相同 .这里发生了两件事情,理解两者都很重要 .
如其他答案中所述,正在调用
Symbol#to_proc
方法 .但是在符号上调用
to_proc
的原因是因为它作为块参数被传递给map
. 在方法调用中将&
放在参数前面会导致它以这种方式传递 . 这适用于任何Ruby方法,而不仅仅是符号的map
.Symbol
被转换为Proc
,因为它作为块传入 . 我们可以通过尝试将proc传递给没有&符号的.map
来显示:即使它不知道如何使用它,因为它需要一个块参数 . 用
&
传递它给出了它期望的块.map
.(&:name)是(&:name.to_proc)的缩写,与
tags.map{ |t| t.name }.join(' ')
相同to_proc实际上是用C实现的
虽然我们已经有了很好的答案,但从初学者的角度来看,我想添加其他信息:
这意味着,您将另一个方法作为参数传递给map函数 . (实际上你传递的是一个符号,它被转换成一个过程 . 但在这个特殊情况下,这并不重要) .
重要的是你有一个
method
名为name
,它将被map方法用作参数而不是传统的block
样式 .这里
:name
是指向标签对象的方法name
的符号 . 当我们将&:name
传递给map
时,它会将name
视为proc对象 . 简而言之,tags.map(&:name)
充当:它的意思是
map(&:name) 获取一个可枚举对象(在您的情况下为标记)并为每个元素/标记运行name方法,输出每个返回值从方法 .
这是一个简写
返回元素(标记)名称的数组