【问题标题】:What does map(&:name) mean in Ruby?Ruby 中的 map(&:name) 是什么意思?
【发布时间】:2010-11-16 01:39:17
【问题描述】:

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

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

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

【问题讨论】:

  • 顺便说一句,我听说这叫做“椒盐卷饼冒号”。
  • 哈哈。我知道这是一个&符号。我从未听说过它被称为“椒盐脆饼”,但这是有道理的。
  • 您也可以去掉括号 tags.map &:name 以获得额外的最短条目。
  • 称它为“椒盐卷饼冒号”是一种误导,虽然很吸引人。红宝石中没有“&:”。与号 (&) 是一个“一元 & 号运算符”,其中包含一个 :symbol。如果有的话,它是一个“椒盐脆饼符号”。只是说。
  • tags.map(&:name) 是从 tags.map{|s| 中排序的s.name}

标签: ruby syntax operators parameter-passing


【解决方案1】:

这是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

【讨论】:

  • tags.map(:name.to_proc) 本身就是 tags.map { |tag| 的简写。标签名}
  • 这不是有效的 ruby​​ 代码,您仍然需要 &,即 tags.map(&:name.to_proc).join(' ')
  • Symbol#to_proc 是在 C 中实现的,而不是在 Ruby 中,但这就是它在 Ruby 中的样子。
  • @AndrewGrimm 它最初是使用该代码添加到 Ruby on Rails 中的。然后在 1.8.7 版本中将其添加为原生 ruby​​ 功能。
  • @SimoneCarletti - 虽然tags.map { |tag| tag.name } 实际上是tags.map(&:name.to_proc) 所做的,但它本身并不完全是简写。这是因为 procs 可以使用 & 运算符转换为块,当它们被传递给使用 yield 并因此需要块的方法时。 (请参阅 Ruby 文档here)。正如 Josh Lee 在上面的帖子中所展示的,符号也可以转换为 procs,然后可以从那里转换为块,这是必要的,因为 map 使用块。
【解决方案2】:

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

array.each(&method(:foo))

这是

的简写
array.each { |element| foo(element) }

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

当你想以 point-free 风格做事时,这非常有用。一个例子是检查数组中是否有任何字符串等于字符串"foo"。有常规的方式:

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

还有免点方式:

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

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

【讨论】:

  • array.each{|e| foo(e)} 仍然更短 :-) +1 反正
  • 你能用&method映射另一个类的构造函数吗?
  • @finishingmove 是的,我猜。试试这个[1,2,3].map(&Array.method(:new))
【解决方案3】:

相当于

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

【讨论】:

    【解决方案4】:
    tags.map(&:name)
    

    相同
    tags.map{|tag| tag.name}
    

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 不,它在 Ruby 1.8.7 及更高版本中。
        • 是 map 的简单习语还是 Ruby 总是以特定方式解释 '&'?
        • @collimarco:正如 jleedev 在他的回答中所说,一元 & 运算符在其操作数上调用 to_proc。所以它并不特定于 map 方法,实际上适用于任何接受块并将一个或多个参数传递给块的方法。
        【解决方案7】:

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

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

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

        【讨论】:

          【解决方案8】:

          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(&amp;:first) 时,Ruby 将第一个输入 [1,'a'] 拆分为 1 和 'a' 以提供 obj 1 和 args* 'a' 导致错误,就像 Fixnum 对象 1 一样没有方法self(即:first)。


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

          1. :first是一个Symbol对象,所以当&amp;:first作为参数给map方法时,Symbol#to_proc被调用。

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

          3. Symbol 类中的 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)表达式相同。

          【讨论】:

          • Josh Lee 的回答绝对是正确的,正如您通过思考 [1,2,3,4,5,6].inject(&amp;:+) 所看到的那样 - inject 需要一个带有两个参数(备忘录和项目)的 lambda 并且 :+.to_proc 提供它 - Proc.new |obj, *args| { obj.send(self, *args) }{ |m, o| m.+(o) }
          【解决方案9】:

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

          to_proc 实际上是用 C 实现的

          【讨论】:

            【解决方案10】:

            ma​​p(&:name) 采用可枚举对象(在您的情况下为标签)并为每个元素/标签运行 name 方法,从该方法输出每个返回值。

            这是一个简写

            array.map { |element| element.name }
            

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

            【讨论】:

              【解决方案11】:

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

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

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

              【讨论】:

                【解决方案12】:

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

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

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

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

                【讨论】:

                  【解决方案13】:

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

                  这是一个简化的 ruby​​ 速记。

                  【讨论】:

                    【解决方案14】:

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

                    tags.map do |tag|
                      tag.name
                    end
                    

                    【讨论】:

                      【解决方案15】:

                      意思是

                      array.each(&:to_sym.to_proc)
                      

                      【讨论】:

                        【解决方案16】:

                        下同:

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

                        【讨论】:

                          猜你喜欢
                          • 2011-04-18
                          • 2016-08-17
                          • 2016-09-17
                          • 2021-11-10
                          • 2011-01-11
                          • 2014-01-28
                          • 1970-01-01
                          • 2013-03-05
                          • 1970-01-01
                          相关资源
                          最近更新 更多