繁体   English   中英

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

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

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

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

map(&:name) (&: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
tags.map(&:name)

是相同的

tags.map{|tag| tag.name}

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

同时让我们注意,&符号#to_proc魔法可以与任何类一起使用,而不仅仅是 Symbol。 许多 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!' ) }

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

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

如其他答案中所述,正在调用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"]

即使不需要转换,该方法也不知道如何使用它,因为它需要一个块参数。 &传递它会给.map它期望的块。

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作为参数提供给 map 方法时,Symbol#to_proc 被调用。

  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. 迭代[[1,'a'],[2,'b'],[3,'c']]对象中的其余元素。

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

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

to_proc 实际上是用 C 实现的

map(&:name)接受一个可枚举对象(在您的情况下是标签)并为每个元素/标签运行 name 方法,输出该方法的每个返回值。

这是一个简写

array.map { |element| element.name }

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

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

其次,虽然&def foo(&block) ... end转换的块传递给foo到一个Proc ,当施加到它的相反Proc

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

尽管我们已经有了很好的答案,但从初学者的角度来看,我想添加其他信息:

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

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

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

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

它是一种简化的 ruby​​ 速记。

用其他词和箭头来表达:

在此处输入图片说明

这里:name是指向标签对象的方法name的符号。 当我们将&:name传递给map ,它会将name视为一个 proc 对象。 简而言之, tags.map(&:name)充当:

tags.map do |tag|
  tag.name
end

它的意思是

array.each(&:to_sym.to_proc)

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

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

my_proc = Proc.new { puts "foo" }

my_method_call(&my_proc) # 等同于: my_method_call { puts "foo" }

它与下面相同:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM