map(&:name)在Ruby中意味着什么?
-
07-07-2019 - |
题
我在 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
其他提示
另一个很酷的简写,很多人都不知道,是
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(:==))
首选方式应该是最易读的方式。
相当于
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
虽然我们还要注意,&符号 #to_proc
魔法可以用于任何类,而不仅仅是符号。许多Rubyist选择在Array类上定义 #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(&:name)
与
相同tags.map{|tag| tag.name}
&:name
只使用符号作为要调用的方法名称。
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。 / p> -
map将调用消息发送到:first.to_proc,参数为
[1,'a']
,例如:first.to_proc.call([1,'a' ])
被执行。
符号类中的 -
to_proc过程使用参数(:first)向数组对象(
[1,'a']
)发送发送消息,例如[1, 'a']。发送(:第一个)
。 -
迭代
[[1,'a'],[2,'b'],[3,'c']]
对象中的其余元素。
醇>
这与执行 [[1,'a'],[2,'b'],[3,'c']]相同.map(| e | e.first)
表达。
这里发生了两件事情,理解这两件事很重要。
如其他答案所述,正在调用 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
,因为它作为一个块传入。我们可以通过尝试将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"]
即使它不需要转换,该方法也不知道如何使用它,因为它需要一个块参数。使用&amp;
传递它会为 .map
提供它所期望的块。
(&amp;:name)是(&amp;:name.to_proc)的缩写,与 tags.map {| t |相同t.name} .join('')
to_proc实际上是用C
实现的虽然我们已经有了很好的答案,但从初学者的角度来看,我想添加其他信息:
map(&amp;:name)在Ruby中意味着什么?
这意味着,您将另一个方法作为参数传递给map函数。 (实际上,你传递的是一个转换成proc的符号。但在这种特殊情况下,这并不重要。)
重要的是你有一个名为 name
的方法
,它将被map方法用作参数而不是传统的块
风格。
map(&amp;:name)获取一个可枚举对象(在您的情况下为标记)并为每个元素/标记运行name方法,从方法中输出每个返回的值。
这是
的简写array.map { |element| element.name }
返回元素(标记)名称数组
此处:name
是指向标记对象的方法 name
的符号。
当我们将&amp;:name
传递给 map
时,它会将 name
视为proc对象。
简而言之, tags.map(&amp;:name)
的作用如下:
tags.map do |tag|
tag.name
end
意味着
array.each(&:to_sym.to_proc)
它基本上对数组中的每个标签执行方法call tag.name
。
这是一个简化的红宝石简写。
与以下相同:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end