我在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)是什么意思?
当前回答
同时让我们也注意到& #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!' ) }
其他回答
它是标签的简写。映射{|标记|tag .name}。加入(' ')
(&:name)是(&:name.to_proc)的缩写,与标签相同。映射{|t| t.name}。加入(' ')
to_proc实际上是用C语言实现的
这里发生了两件事,理解这两件事很重要。
正如在其他回答中所描述的,正在调用Symbol#to_proc方法。
但是符号上调用to_proc的原因是因为它被作为块参数传递给map。将&放在方法调用中的参数前面会导致它以这种方式传递。这适用于任何Ruby方法,而不仅仅是带有符号的映射。
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"]
即使它不需要转换,方法也不知道如何使用它,因为它期望一个块参数。给它传递&给.map它所期望的块。
同时让我们也注意到& #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!' ) }
它等价于
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end