【问题标题】:Implicit block passing and dynamically defined methods隐式块传递和动态定义的方法
【发布时间】:2014-10-12 15:24:22
【问题描述】:

请考虑以下不同的方式来定义方法m

  • 方法一:

    class C
      def m; yield; end
    end
    
  • 方法二:

    class C
      def initialize
        (class << self; self; end).class_eval do            
          define_method(:m){|&b| b.call }
        end
      end
    end
    
  • 方法三:

    class C
      def initialize
        (class << self; self; end).class_eval do            
          define_method(:m){puts block_given?; yield}            
        end
      end
    end
    

然后我可以使用Object#send 调用m

o = C.new
o.send(:m) {puts 'test'}

虽然使用方法 1 或方法 2 调用 m 可以正常工作,但方法 3 会出现此错误:

no block given (yield) (LocalJumpError)

我知道块不是一个对象,而只是方法调用语法的一部分,如果不写一些晦涩的东西,就不能将隐式块从一个函数传递到另一个函数:

def printer
  yield
end
def proxy
  printer &Proc.new
end

proxy { puts "&Proc.new probably creates Proc object from block"}

但是在这种情况下,为什么方法 1 有效?得到一个可以解释幕后发生的事情的答案真是太棒了。

【问题讨论】:

  • 不清楚您所说的“静态定义 ...”、“使用显式块”或“第一种情况”是什么意思。我猜你可能会提到你的方法 1、方法 2 或方法 3,但不清楚是哪个。
  • @sawa 我认为代码片段会很清楚。您分别引用了方法一、方法二和方法一:)
  • 你为什么用send打电话给m?它看起来没有任何意义。
  • @sawa 这个代码是从上下文中派生的,它应该被这样调用,但在这种特殊情况下,它似乎并不重要。
  • 如果您不知道,在方法 2 中,您可以在类上调用 class_eval,而不是在单例类上:self.class.class_eval do

标签: ruby metaprogramming proc


【解决方案1】:

方法 3 的问题在于范围。 yield 是指传递给 initialize 的块,如果有的话。它不引用传递给m 的块。而且由于您通过 C.new 创建了 o 而没有阻止,因此预期的 yield 不存在,这会导致错误。

在方法 1 中,yield 指的是传递给m 的块。在方法 2 中,b 指的是从传递给 m 的块转换而来的 proc。

【讨论】:

  • 啊,我明白了!但是是否可以在动态定义的方法中使用yield
  • this
猜你喜欢
  • 1970-01-01
  • 2023-03-15
  • 2021-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-08
  • 1970-01-01
相关资源
最近更新 更多