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

What does map(&:name) mean in Ruby?

提问人:collimarco 提问时间:8/2/2009 最后编辑:user513951collimarco 更新时间:9/12/2022 访问量:154232

问:

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

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

the in 是什么意思?(&:name)map(&:name)

Ruby 语法 运算符 参数传递

评论

139赞 Josh Lee 10/8/2010
顺便说一句,我听说过这叫做“椒盐脆饼冒号”。
7赞 DragonFax 2/19/2013
哈哈。我知道这一点,作为一个 Ampersand。我从未听说过它被称为“椒盐脆饼”,但这是有道理的。
1赞 itsnikolay 6/10/2013
此外,您可以删除括号以获得更短的条目。tags.map &:name
81赞 fontno 7/2/2013
称它为“椒盐卷饼冒号”具有误导性,尽管朗朗上口。红宝石中没有“&:”。与号 (&) 是一个“一元与号运算符”,带有一个推在一起的 :符号。如果有的话,它是一个“椒盐卷饼符号”。只是说。
4赞 kaushal sharma 7/17/2016
tags.map(&:name) 是从 tags.map{|s| s.name} 排序

答:

41赞 Oliver N. 8/2/2009 #1

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

评论

0赞 Chuck 8/2/2009
不,它在 Ruby 1.8.7 及更高版本中。
0赞 collimarco 8/2/2009
它是 map 的简单成语还是 Ruby 总是以特定的方式解释“&”?
7赞 Chuck 8/2/2009
@collimarco:正如 jleedev 在他的回答中所说,一元运算符调用其操作数。因此,它并不特定于 map 方法,实际上适用于任何接受块并将一个或多个参数传递给块的方法。&to_proc
84赞 Sophie Alpert 8/2/2009 #2

它相当于

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end
548赞 Josh Lee 8/2/2009 #3

它是tags.map(&:name.to_proc).join(' ')

如果是一个带有方法的对象,那么你可以把它传递给一个方法,作为 ,该方法将调用并将其用作方法的块。footo_proc&foofoo.to_proc

该方法最初是由 ActiveSupport 添加的,但已集成到 Ruby 1.8.7 中。这是它的实现:Symbol#to_proc

class Symbol
  def to_proc
    Proc.new do |obj, *args|
      obj.send self, *args
    end
  end
end

评论

104赞 Simone Carletti 8/2/2009
tags.map(:name.to_proc) 本身就是 tags.map { |tag| tag.name } 的简写
6赞 horseyguy 6/25/2011
这不是有效的 Ruby 代码,您仍然需要 ,即&tags.map(&:name.to_proc).join(' ')
5赞 Andrew Grimm 7/4/2011
Symbol#to_proc 是用 C 语言实现的,而不是在 Ruby 中实现的,但这就是它在 Ruby 中的样子。
5赞 Cameron Martin 2/14/2013
@AndrewGrimm,它最初是使用该代码添加到 Ruby on Rails 中的。然后,它被添加为版本 1.8.7 中的原生 ruby 功能。
4赞 jazzyfresh 8/3/2013
@SimoneCarletti - 虽然实际上是有效的,但它本身并不完全是速记。这是因为当 proc 传递给使用 yield 并因此需要块的方法时,可以使用 & 运算符将其转换为块。(请参阅此处的 Ruby 文档)。正如 Josh Lee 在上面的帖子中所展示的那样,符号也可以转换为过程,然后从那里可以转换为块,这是必要的,因为地图使用块。tags.map { |tag| tag.name }tags.map(&:name.to_proc)
199赞 Gerry 3/9/2012 #4

另一个很酷的速记,许多人不知道,是

array.each(&method(:foo))

这是

array.each { |element| foo(element) }

通过调用,我们从中获取了一个对象,该对象表示其方法,并使用 表示它有一个方法将其转换为 .method(:foo)Methodselffoo&to_procProc

当您想以无点风格做事时,这非常有用。例如,检查数组中是否有任何字符串等于字符串。有一种常规方法:"foo"

["bar", "baz", "foo"].any? { |str| str == "foo" }

还有一种无点的方式:

["bar", "baz", "foo"].any?(&"foo".method(:==))

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

评论

31赞 Jared Beck 5/16/2012
array.each{|e| foo(e)}仍然更短:-) +1 反正
0赞 holographic-principle 2/5/2015
你能用吗?&method
4赞 Gerry 2/6/2015
@finishingmove是的,我猜。试试这个[1,2,3].map(&Array.method(:new))
0赞 Cadoiz 11/18/2022
剧透:结果是 - 这可能会很有用。[[nil], [nil, nil], [nil, nil, nil]]
51赞 Boris Stitnicky 11/20/2012 #5

同时,我们也要注意,与号和魔术可以与任何类一起使用,而不仅仅是符号。许多 Ruby 主义者选择在 Array 类上定义:#to_proc#to_proc

class Array
  def to_proc
    proc { |receiver| receiver.send *self }
  end
end

# And then...

[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]

与号的工作原理是在其操作数上发送消息,在上面的代码中,该操作数属于 Array 类。由于我在 Array 上定义了方法,因此该行变为:&to_proc#to_proc

[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
15赞 prosseek 1/24/2014 #6

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

使用此代码,在执行时,Ruby 将第一个输入拆分为 1 和 'a' 以给出 1 和 'a' 以导致错误,因为 Fixnum 对象 1 没有 self 方法(即 :first)。print [[1,'a'],[2,'b'],[3,'c']].map(&:first)[1,'a']objargs*


何时执行;[[1,'a'],[2,'b'],[3,'c']].map(&:first)

  1. :first是一个 Symbol 对象,因此当将 map 方法作为参数提供给 Symbol#to_proc 时,将调用 Symbol#。&:first

  2. map 使用参数向 :first.to_proc 发送调用消息,例如,执行。[1,'a']:first.to_proc.call([1,'a'])

  3. 例如,Symbol 类中的to_proc过程向带有参数 (:first) 的数组对象 () 发送消息。[1,'a'][1,'a'].send(:first)

  4. 遍历 object 中的其余元素。[[1,'a'],[2,'b'],[3,'c']]

这与执行表达式相同。[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)

评论

2赞 Uri Agassi 5/20/2014
Josh Lee 的答案是绝对正确的,正如你通过思考所看到的那样 - inject 期望一个具有两个参数(memo 和 item)的 lambda 并交付它 - 或者[1,2,3,4,5,6].inject(&:+):+.to_procProc.new |obj, *args| { obj.send(self, *args) }{ |m, o| m.+(o) }
18赞 devpuppy 4/9/2016 #7

这里正在发生两件事,了解这两件事很重要。

如其他答案中所述,正在调用该方法。Symbol#to_proc

但是在符号上调用的原因是因为它被作为块参数传递给。在方法调用中,将参数放在参数的前面会导致以这种方式传递它。这适用于任何 Ruby 方法,而不仅仅是符号。to_procmap&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)

它被转换为 a,因为它是作为块传入的。我们可以通过尝试在没有 & 符号的情况下传递一个 proc 来证明这一点:SymbolProc.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

-1赞 Naveen Kumar 6/27/2016 #8

如下同:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
1赞 timlentse 6/30/2016 #9

这是指向标记对象方法的符号。 当我们传递给 时,它将被视为一个 proc 对象。 简称,充当::namename&:namemapnametags.map(&:name)

tags.map do |tag|
  tag.name
end
5赞 tessie 8/25/2016 #10

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

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

69赞 Albert.Qing 11/1/2016 #11
tags.map(&:name)

tags.map{|tag| tag.name}

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

评论

1赞 Qasim 12/2/2022
我可以用多个值来做到这一点吗,比如 .tags.map(&:name, &:id)
1赞 Albert.Qing 12/5/2022
@Qasim 你做不到,但你可以这样做tags.map { |t| t.name.to_s << t.age.to_s }
1赞 DDD 12/20/2016 #12

这意味着

array.each(&:to_sym.to_proc)
4赞 Jonathan Duarte 12/8/2017 #13

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

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

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

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

5赞 Sunda 9/1/2018 #14

map(&:name) 接受一个可枚举对象(在本例中为 tags)并为每个元素/标记运行 name 方法,输出该方法的每个返回值。

它是

array.map { |element| element.name }

它返回 element(tag) 名称的数组

2赞 Olalekan Sogunle 2/13/2019 #15

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

它是一种简化的红宝石速记。

5赞 Christoph 3/16/2020 #16

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

其次,当 将传递给 的块转换为 时,当应用于 .&def foo(&block) ... endfooProcProc

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

2赞 MD Shahid Khan 9/12/2022 #17

Ruby 中没有 &: 运算符。您看到的是应用于 :symbol 的 & 运算符。

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

my_proc = Proc.new { 放 “foo” }

my_method_call(&my_proc) # 等同于: my_method_call { 放 “foo” }