map(&:name)在Ruby中是什么意思?

我在一个RailsCast中找到了这段代码:

def tag_names
@tag_names || tags.map(&:name).join(' ')
end

map(&:name)中的(&:name)是什么意思?

147860 次浏览

它是tags.map { |tag| tag.name }.join(' ')的简写

它等价于

def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end

它是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(:==))

首选的方式应该是最易读的方式。

同时让我们也注意到& #to_proc魔法可以用于任何类,而不仅仅是符号。很多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!"]

& &通过在其操作数上发送to_proc消息来工作,在上面的代码中,该操作数属于Array类。因为我在Array上定义了#to_proc方法,所以这一行变成:

[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }

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)时;

  1. :first是一个Symbol对象,因此当&:first作为参数被赋予一个映射方法时,Symbol#to_proc将被调用。

  2. 地图将呼叫信息发送到:first。执行参数为[1,'a']的to_proc,例如:first.to_proc.call([1,'a'])

  3. 符号类中的to_proc过程向数组对象([1,'a'])发送一个发送消息,参数为(:first),例如,[1,'a'].send(:first)被执行。

  4. 遍历[[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,因为它是作为块传入的。我们可以通过尝试传递一个不带&号的进程给.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它所期望的块。

如下所示:

def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end
这里:name是指向标签对象的方法name的符号。 当我们将&:name传递给map时,它将把name作为一个proc对象。 总之,tags.map(&:name)的作用是:

tags.map do |tag|
tag.name
end

(&:name)是(&:name.to_proc)的缩写,与tags.map{ |t| t.name }.join(' ')相同

to_proc实际上是用C语言实现的

tags.map(&:name)

是一样的

tags.map{|tag| tag.name}

&:name只是使用符号作为要调用的方法名。

它的意思是

array.each(&:to_sym.to_proc)

虽然我们已经有了很好的答案,但从初学者的角度来看,我想添加额外的信息:

map(&:name)在Ruby中是什么意思?

这意味着,您正在将另一个方法作为参数传递给map函数。 (实际上你传递的是一个符号,它会被转换成一个过程。但在这个特殊的情况下,这并不那么重要)

重要的是,您有一个名为namemethod,它将被map方法用作参数,而不是传统的block样式。

地图(及名称):接受一个可枚举对象(在您的例子中是标签),并为每个元素/标签运行name方法,输出该方法的每个返回值。

这是一个简写

array.map { |element| element.name }

返回元素(标签)名称的数组

它基本上在数组中的每个标记上执行方法调用tag.name

它是一个简化的ruby简写。

首先,&:name&:name.to_proc的快捷方式,其中:name.to_proc返回Proc(类似于lambda,但不完全相同),当以对象作为(第一个)参数调用时,将调用该对象上的name方法。

其次,虽然def foo(&block) ... end中的&将传递给foo的块转换为Proc,但应用到Proc时则相反。

因此,&:name.to_proc是一个以对象为参数并在其上调用name方法的块,即{ |o| o.name }

Ruby中没有&:运算符。你所看到的是&运算符应用于:符号。

在方法参数列表中,&operator获取其操作数,如果还没有将其转换为Proc对象(通过对其调用to_proc),并将其传递给方法,就像使用了块一样。

my_proc = Proc.new {put " food "}

my_method_call(&my_proc) #与: My_method_call {put " food "} < / p >