我在RailsCast中发现了这段代码:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
map(&:name)
中的 (&:name)
是什么意思?
我在RailsCast中发现了这段代码:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
map(&:name)
中的 (&: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
&
符号,即 tags.map(&:name.to_proc).join(' ')
。 - horseyguytags.map { |tag| tag.name }
与 tags.map(&:name.to_proc)
效果相同,但前者并不是简写。这是因为当一个方法需要一个块时(参考 Ruby 文档 此处),procs 可以使用 & 运算符转换成块。正如 Josh Lee 在上面的帖子中所示,符号也可以转换为 procs ,然后再转换为块,这是必要的,因为 map 使用的就是块。 - jazzyfresh还有一个很酷的简写方式,不为人知,它是
array.each(&method(:foo))
这是一个简写形式,表示
array.each { |element| foo(element) }
method(:foo)
,我们从self
中取出表示其foo
方法的Method
对象,并使用&
表示它有一个to_proc
method,将其转换为Proc
。"foo"
。 有常规方式:["bar", "baz", "foo"].any? { |str| str == "foo" }
还有一种无点方式:
["bar", "baz", "foo"].any?(&"foo".method(:==))
array.each{|e| foo(e)}
仍然更短 :-) 无论如何+1 - Jared Beck&method
映射另一个类的构造函数吗? - holographic-principle[1,2,3].map(&Array.method(:new))
。 - Gerry[[nil],[nil,nil],[nil,nil,nil]]
- 这可能会很有用。 - Cadoiz它等价于
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
tags.map(&:name)
就是
tags.map{|tag| tag.name}
&:name
只是使用该符号作为要调用的方法名称。
tags.map(&:name, &:id)
。 - Qasimtags.map { |t| t.name.to_s << t.age.to_s }
。 - Albert.Qing请注意,符号“&”的#to_proc
魔法可以与任何类一起使用,而不仅仅是符号类。许多Ruby程序员选择在数组类上定义#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(' ')
中的所有标签名字用空格连接起来。&
会对其操作数调用to_proc
方法。因此,它不仅适用于map方法,实际上可以用于任何需要将一个或多个参数传递给块的方法。 - Chuckdef 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
可以提供所需的块。除了等价的Ruby代码应该如下所示,Josh Lee的答案几乎是正确的。
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发送call消息并传递参数[1,'a']
,例如执行:first.to_proc.call([1,'a'])
。
Symbol类中的to_proc过程向一个数组对象([1,'a']
)发送send消息,并传递参数(: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(&:+)
来看出来 - inject期望一个带有两个参数(memo和item)的lambda表达式,而:+.to_proc
提供了它 - Proc.new |obj, *args| { obj.send(self, *args) }
或{ |m, o| m.+(o) }
。 - Uri Agassimap(&:name)将一个可枚举对象(在您的情况下为标签)作为输入,并针对每个元素/标签运行name方法,输出从该方法返回的值。
这是一种简写形式:
array.map { |element| element.name }
该函数返回元素(标记)名称的数组。
(&:name) 是 (&:name.to_proc) 的简写,它与 tags.map{ |t| t.name }.join(' ')
相同。
to_proc 实际上是用 C 实现的。
tags.map &:name
以获得更短的条目。 - itsnikolay