[英]What does map(&:name) mean in Ruby?
I found this code in a RailsCast :我在RailsCast 中找到了这段代码:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
What does the (&:name)
in map(&:name)
mean? map(&:name)
(&:name)
中的 (&:name) 是什么意思?
It's shorthand for tags.map(&:name.to_proc).join(' ')
它是
tags.map(&:name.to_proc).join(' ')
的简写
If foo
is an object with a to_proc
method, then you can pass it to a method as &foo
, which will call foo.to_proc
and use that as the method's block.如果
foo
是具有to_proc
方法的对象,那么您可以将其作为&foo
传递给方法,该方法将调用foo.to_proc
并将其用作方法的块。
The Symbol#to_proc
method was originally added by ActiveSupport but has been integrated into Ruby 1.8.7. Symbol#to_proc
方法最初由 ActiveSupport 添加,但已集成到 Ruby 1.8.7 中。 This is its implementation:这是它的实现:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Another cool shorthand, unknown to many, is另一个很酷的速记,很多人都不知道,是
array.each(&method(:foo))
which is a shorthand for这是一个简写
array.each { |element| foo(element) }
By calling method(:foo)
we took a Method
object from self
that represents its foo
method, and used the &
to signify that it has a to_proc
method that converts it into a Proc
.通过调用
method(:foo)
我们从self
中获取了一个Method
对象来表示它的foo
方法,并使用&
来表示它有一个to_proc
方法可以将它转换成一个Proc
。
This is very useful when you want to do things point-free style.当您想做无点风格的事情时,这非常有用。 An example is to check if there is any string in an array that is equal to the string
"foo"
.一个例子是检查数组中是否有任何字符串等于字符串
"foo"
。 There is the conventional way:有一个常规的方法:
["bar", "baz", "foo"].any? { |str| str == "foo" }
And there is the point-free way:还有一种无点的方式:
["bar", "baz", "foo"].any?(&"foo".method(:==))
The preferred way should be the most readable one.首选方式应该是最易读的方式。
It's equivalent to它相当于
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
tags.map(&:name)
is The same as是相同的
tags.map{|tag| tag.name}
&:name
just uses the symbol as the method name to be called. &:name
只是使用符号作为要调用的方法名称。
While let us also note that ampersand #to_proc
magic can work with any class, not just Symbol.同时让我们注意,&符号
#to_proc
魔法可以与任何类一起使用,而不仅仅是 Symbol。 Many Rubyists choose to define #to_proc
on Array class:许多 Ruby 专家选择在 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 &
works by sending to_proc
message on its operand, which, in the above code, is of Array class. &
通过在其操作数上发送to_proc
消息来工作,在上面的代码中,该操作数属于 Array 类。 And since I defined #to_proc
method on Array, the line becomes:由于我在 Array 上定义了
#to_proc
方法,因此该行变为:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
It's shorthand for tags.map { |tag| tag.name }.join(' ')
它是
tags.map { |tag| tag.name }.join(' ')
的简写tags.map { |tag| tag.name }.join(' ')
Two things are happening here, and it's important to understand both.这里发生了两件事,理解这两件事很重要。
As described in other answers, the Symbol#to_proc
method is being called.如其他答案中所述,正在调用
Symbol#to_proc
方法。
But the reason to_proc
is being called on the symbol is because it's being passed to map
as a block argument.但是在符号上调用
to_proc
的原因是因为它作为块参数传递给map
。 Placing &
in front of an argument in a method call causes it to be passed this way.将
&
放在方法调用中的参数前面会导致它以这种方式传递。 This is true for any Ruby method, not just map
with symbols.对于任何 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)
The Symbol
gets converted to a Proc
because it's passed in as a block. Symbol
被转换为Proc
因为它是作为一个块传入的。 We can show this by trying to pass a proc to .map
without the ampersand:我们可以通过尝试将 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"]
Even though it doesn't need to be converted, the method won't know how to use it because it expects a block argument.即使不需要转换,该方法也不知道如何使用它,因为它需要一个块参数。 Passing it with
&
gives .map
the block it expects.用
&
传递它会给.map
它期望的块。
Josh Lee's answer is almost correct except that the equivalent Ruby code should have been as follows. Josh Lee 的回答几乎是正确的,只是等效的 Ruby 代码应该如下所示。
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
not不是
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
With this code, when print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
is executed, Ruby splits the first input [1,'a']
into 1 and 'a' to give obj
1 and args*
'a' to cause an error as Fixnum object 1 does not have the method self (which is :first).使用这段代码,当执行
print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
,Ruby 会拆分第一个输入[1,'a']
到 1 和 'a' 给obj
1 和args*
'a' 导致错误,因为 Fixnum 对象 1 没有方法 self (即 :first)。
When [[1,'a'],[2,'b'],[3,'c']].map(&:first)
is executed;当
[[1,'a'],[2,'b'],[3,'c']].map(&:first)
被执行时;
:first
is a Symbol object, so when &:first
is given to a map method as a parameter, Symbol#to_proc is invoked. :first
是一个 Symbol 对象,所以当&:first
作为参数提供给 map 方法时,Symbol#to_proc 被调用。
map sends call message to :first.to_proc with parameter [1,'a']
, eg, :first.to_proc.call([1,'a'])
is executed. map 将调用消息发送到 :first.to_proc 并带有参数
[1,'a']
,例如:first.to_proc.call([1,'a'])
被执行。
to_proc procedure in Symbol class sends a send message to an array object ( [1,'a']
) with parameter (:first), eg, [1,'a'].send(:first)
is executed. Symbol 类中的 to_proc 过程向带有参数 (:first) 的数组对象 (
[1,'a']
) 发送发送消息,例如,执行[1,'a'].send(:first)
。
iterates over the rest of the elements in [[1,'a'],[2,'b'],[3,'c']]
object.迭代
[[1,'a'],[2,'b'],[3,'c']]
对象中的其余元素。
This is the same as executing [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
expression.这与执行
[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
表达式相同。
(&:name) is short for (&:name.to_proc) it is same as tags.map{ |t| t.name }.join(' ')
(&:name) 是 (&:name.to_proc) 的缩写,与
tags.map{ |t| t.name }.join(' ')
tags.map{ |t| t.name }.join(' ')
to_proc is actually implemented in C to_proc 实际上是用 C 实现的
map(&:name) takes an enumerable object (tags in your case) and runs the name method for each element/tag, outputting each returned value from the method. map(&:name)接受一个可枚举对象(在您的情况下是标签)并为每个元素/标签运行 name 方法,输出该方法的每个返回值。
It is a shorthand for这是一个简写
array.map { |element| element.name }
which returns the array of element(tag) names它返回元素(标签)名称的数组
First, &:name
is a shortcut for &:name.to_proc
, where :name.to_proc
returns a Proc
(something that is similar, but not identical to a lambda) that when called with an object as (first) argument, calls the name
method on that object.首先,
&:name
是&:name.to_proc
的快捷方式,其中:name.to_proc
返回一个Proc
(与 lambda 类似,但不完全相同),当使用对象作为(第一个)参数调用时,调用该对象上的name
方法。
Second, while &
in def foo(&block) ... end
converts a block passed to foo
to a Proc
, it does the opposite when applied to a Proc
.其次,虽然
&
在def foo(&block) ... end
转换的块传递给foo
到一个Proc
,当施加到它的相反Proc
。
Thus, &:name.to_proc
is a block that takes an object as argument and calls the name
method on it, ie { |o| o.name }
因此,
&:name.to_proc
是一个将对象作为参数并对其调用name
方法的块,即{ |o| o.name }
{ |o| o.name }
. { |o| o.name }
.
Although we have great answers already, looking through a perspective of a beginner I'd like to add the additional information:尽管我们已经有了很好的答案,但从初学者的角度来看,我想添加其他信息:
What does map(&:name) mean in Ruby?
map(&:name) 在 Ruby 中是什么意思?
This means, that you are passing another method as parameter to the map function.这意味着,您将另一个方法作为参数传递给 map 函数。 (In reality you're passing a symbol that gets converted into a proc. But this isn't that important in this particular case).
(实际上,您正在传递一个可以转换为 proc 的符号。但这在这种特殊情况下并不那么重要)。
What is important is that you have a method
named name
that will be used by the map method as an argument instead of the traditional block
style.重要的是你有一个名为
name
的method
,它会被 map 方法用作参数,而不是传统的block
样式。
It basically execute the method call tag.name
on each tags in the array.它基本上对数组中的每个标签执行方法调用
tag.name
。
It is a simplified ruby shorthand.它是一种简化的 ruby 速记。
Here :name
is the symbol which point to the method name
of tag object.这里
:name
是指向标签对象的方法name
的符号。 When we pass &:name
to map
, it will treat name
as a proc object.当我们将
&:name
传递给map
,它会将name
视为一个 proc 对象。 For short, tags.map(&:name)
acts as:简而言之,
tags.map(&:name)
充当:
tags.map do |tag|
tag.name
end
它的意思是
array.each(&:to_sym.to_proc)
There isn't a &: operator in Ruby. Ruby 中没有 &: 运算符。 What you are seeing is the & operator applied to a:symbol.
您看到的是应用于 a:symbol 的 & 运算符。
In a method argument list, the & operator takes its operand, converts it to a Proc object if it isn't already (by calling to_proc on it) and passes it to the method as if a block had been used.在方法参数列表中,& 运算符获取其操作数,如果还没有(通过调用 to_proc)将其转换为 Proc object,并将其传递给方法,就像使用了块一样。
my_proc = Proc.new { puts "foo" } my_proc = Proc.new { puts "foo" }
my_method_call(&my_proc) # is identical to: my_method_call { puts "foo" } my_method_call(&my_proc) # 等同于: my_method_call { puts "foo" }
It is same as below:它与下面相同:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.