简体   繁体   English

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

[英]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)被执行时;

  1. :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 被调用。

  2. 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'])被执行。

  3. 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)

  4. 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.重要的是你有一个名为namemethod ,它会被 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​​ 速记。

To put it with other words and arrows:用其他词和箭头来表达:

在此处输入图片说明

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.

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