我在RailsCast中找到了以下代码:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
是什么在(&:name)
中map(&:name)
是什么意思?
我在RailsCast中找到了以下代码:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
是什么在(&:name)
中map(&:name)
是什么意思?
Answers:
这是简写 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
&
,即tags.map(&:name.to_proc).join(' ')
许多人不知道的另一个很酷的速记是
array.each(&method(:foo))
这是的简写
array.each { |element| foo(element) }
通过调用method(:foo)
我们采取了Method
对象从self
代表其foo
方法,以及所使用的&
,以表明它有一个to_proc
方法,它转换成一个Proc
。
当您想做无指向性的样式时,这非常有用。一个示例是检查数组中是否有任何与string相等的字符串"foo"
。有常规方法:
["bar", "baz", "foo"].any? { |str| str == "foo" }
还有一种无意义的方式:
["bar", "baz", "foo"].any?(&"foo".method(:==))
首选方式应该是最易读的方式。
array.each{|e| foo(e)}
仍然更短:-)反正+1
&method
吗?
[1,2,3].map(&Array.method(:new))
相当于
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
同时让我们注意,“&”号#to_proc
魔术符可以用于任何类别,而不仅限于Symbol。许多Rubyist选择#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!"]
“与”号&
通过to_proc
在其操作数上发送消息来工作,在上面的代码中,操作数是Array类。并且由于我#to_proc
在Array上定义了方法,因此该行变为:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
这是简写 tags.map { |tag| tag.name }.join(' ')
&
符调用to_proc
其操作数。因此,它并不特定于map方法,并且实际上适用于采用一个块并将一个或多个参数传递给该块的任何方法。
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)
被执行;
:first
是Symbol对象,因此当&:first
将map方法作为参数提供给对象时,将调用Symbol#to_proc。
map通过参数将呼叫消息发送到:first.to_proc [1,'a']
,例如,:first.to_proc.call([1,'a'])
已执行。
Symbol类中的to_proc过程将发送消息发送到[1,'a']
带有参数(:first)的数组对象(),例如[1,'a'].send(:first)
已执行。
遍历[[1,'a'],[2,'b'],[3,'c']]
对象中的其余元素。
这与执行[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
表达式相同。
[1,2,3,4,5,6].inject(&:+)
–注入期望具有两个参数(备注和项)的lambda :+.to_proc
并将其传递- Proc.new |obj, *args| { obj.send(self, *args) }
或{ |m, o| m.+(o) }
这里发生了两件事,理解这两者很重要。
如其他答案所述,该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
将其转换Proc
为一个。我们可以通过尝试在.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
它预计该块。
如下:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end