【问题标题】:Ruby Koans about message passing "send" block & argumentsRuby Koans 关于消息传递“发送”块和参数
【发布时间】:2012-12-12 10:41:52
【问题描述】:

我正在研究 Ruby Koans about_message_passing.rb 并获得了适用于 method_missing 的代码,如下所示:

def method_missing(method_name, *args, &block)
  @messages << method_name
  @object.__send__(method_name, *args, &block)
end

这段代码似乎可以工作,但我不太明白为什么 *args 中需要 splat 而块中需要 &。

如果我在定义一个方法,我知道 * 和 & 分别用于表示数组参数和块参数,但是当它们与 send 方法一起用于调用对象上的方法时,这意味着什么?

【问题讨论】:

    标签: ruby methods block send splat


    【解决方案1】:

    我会一次拿这些。完全摆脱method_missing,因为它只会让正在发生的事情变得混乱。这实际上与此完全无关。


    splat * 做了两件事。在方法定义的参数中,它将多个参数吸收到一个数组中。当在方法 invocation 中使用时,它会将一个数组分解为单独的参数。同时使用这两者允许您将任意数量的参数转发给另一个方法。

    def foo(*args)
      bar(*args)
    end
    
    def bar(a, b, c)
      puts a
      puts b
      puts c
    end
    
    foo(1,2,3) # prints 1, 2 and then 3
    

    由于您基本上是在转发所有参数,因此这是相同的模式。


    &amp; 用于块参数。每个方法调用都可以有其中一个,它是挂在最后的块。这是一个特殊的论点,因为它不直接进入论点。您可以通过捕获 add &amp;someblock 作为方法定义中的最后一个参数来将块捕获到变量。

    然后您可以使用相同的语法在方法调用中传递一个块。

    def foo(&block)
      bar(&block)
    end
    
    def bar
      yield
    end
    
    foo { puts 'hello' } # prints hello
    

    这允许您将挂起的块传递给另一个方法,而无需调用它。它并不总是必需的,因为您通常只使用yield 来执行传递的任何块。但是如果你想做一些事情而不只是执行它,你需要捕获对块本身的引用。


    因此,如果您将这两件事结合起来,您将获得最终的方法转发器。您捕获所有任意数量的参数,以及任何挂在末尾的块,并将它们发送到另一个方法。

    # forwards everything to the method `bar`
    def foo(*args, &block)
      bar(*args, &block)
    end
    

    最后,send 只是一种方法。它需要一个方法的名称,后跟任意数量的参数(不是数组),并且可以选择处理挂起的块。

    换句话说:

    foo.send methodName, *args, &block
    

    【讨论】:

    • 请注意,splat 不仅仅适用于方法参数定义/参数列表。它也适用于块参数定义/参数列表,赋值。
    【解决方案2】:

    方法定义中的 splat 表示“获取所有不匹配的参数并将它们放入一个数组中”(在 ruby​​ 1.8 中,这始终是最后一个参数,但在 1.9 中,splats 可能出现在中间)。

    在方法调用中使用它是相反的:这意味着获取这个数组并将其内容用作参数

    foo(a,b) #call foo with 2 arguments: a and b
    foo([a,b]) #call foo with a single array argument 
    foo(*[a,b]) # call foo with 2 arguments: a and b
    

    & 类似:在方法定义中,它捕获块并将其转换为 proc,但在方法调用中,它会将 proc(或类似 proc 的对象 - 任何响应 to_proc 的对象都可以)转换为块那个方法

    method_missing 需要这两个参数,因为(通常)您希望传递原始方法调用中的所有参数和块。

    【讨论】:

      【解决方案3】:

      据我所知,只要你直接传递一个块,它的语法就是 &block_name。

      此外,Object#send 的方法签名采用无限参数,而不是数组。因此,通过传递 splatted 值 *args,它与传递逗号分隔的 args 相同。

      【讨论】:

        猜你喜欢
        • 2015-03-15
        • 1970-01-01
        • 2017-06-21
        • 2020-03-15
        • 2012-05-30
        • 1970-01-01
        • 2017-02-07
        • 1970-01-01
        • 2015-02-18
        相关资源
        最近更新 更多