【问题标题】:Could someone explain please how the code works有人可以解释一下代码是如何工作的
【发布时间】:2019-10-16 14:14:32
【问题描述】:

有一些代码:

def func
   def func
      1
   end
end

然后我在irb 中尝试以下操作:

func
func.func
func

并得到结果:

:func
1
1

谁能解释一下发生了什么?我有点理解第一个输出,但不是后者。谢谢!

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

    您在全局范围内的方法内定义方法。方法定义返回一个符号及其名称。

    1. 当您第一次调用func 时,它会被内部func 重新定义。这就是为什么随后对func 的调用返回1
    2. 方法定义返回一个符号,您可以在该符号上调用任何全局定义的方法,这就是您可以调用func.func 的原因。尝试定义其他方法,您将能够在任何符号上调用它:
    def func
       def func
          1
       end
    end
    def a
      'a'
    end
    func.a
    # 'a'
    :asd.a
    # 'a'
    

    【讨论】:

    • 我正在尝试在func 上调用a,它返回private method 'a' called for :func:Symbol (NoMethodError),Ruby 2.6.1p33 [x86_64-darwin18]。
    • 请粘贴您要执行的操作。
    • 与您的答案 repl.it/@vnhnhm/PureBlaringSymbols 中的内容完全相同(这次也是 Ruby 2.5.5p157)。
    • 尝试使用 Ruby 2.5.1p57(2018-03-29 修订版 63029)[x86_64-darwin18],同样的错误。
    • @sofi 关键是如果你在 irb 全局命名空间中定义一个方法,它就会被添加到几乎所有 Ruby 对象都包含的 Kernel 中。 1.a 可以工作,:foo.a 可以工作,"foo".a 可以通过在全局命名空间中定义 a 方法来工作。当调用foo.foo 时,您只需在第一个foo 调用的返回值上调用foo
    【解决方案2】:

    这很复杂。这确实不应该在 Ruby 中工作,但确实可以。

    def func 在顶层定义了一个全局方法。但是这在 Ruby 中是如何工作的呢?它实际上使用了两个技巧:

    1. 它将方法添加到Object,以便您可以从任何地方调用它(因为self 始终是Object
    2. 它创建了private 方法,因为私有方法的规则是它们不能有“显式接收器”,即您必须调用它们而不在它们前面放置对象和点。这是为了防止出现错误,例如我认为类 Foo 有自己的 puts 方法,但实际上没有,Foo.new.puts 会引发错误,而不是调用全局 puts

    所以如果你的代码只是

    def func
      1
    end
    
    1.func
    

    它会崩溃,因为您试图在对象 1 上调用私有方法。

    但是这就是奇怪的地方。如果你在另一个方法中定义一个方法,它定义了一个普通实例方法,就好像它根本没有嵌套一样

    class A
      def outer
        def inner
          3
        end
      end
    end
    
    x = A.new
    y = A.new
    x.outer
    y.inner # calls the method defined by x
    

    这并不是 Ruby 中真正的有意设计,而更像是一种奇怪情况的后备方案。 lead designer 不喜欢它甚至可能

    ...嵌套方法定义的当前行为是无用的。它应该过时以开辟未来的可能性(我会投票赞成警告)。

    你的代码表现得如此奇怪,因为你在顶层执行它,self 只是一个Object

    def func # normal private method in Object
      def func # adds a normal instance method to the current class i.e. Object
        1
      end
    end
    
    Object.private_methods.include?(:func) # true
    func # returns :func, but also now re-defines func to be a normal method on Object
    Object.private_methods.include?(:func) # false
    func.func # same as 1.func, which is OK because it's not private
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-31
      • 2011-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-21
      • 2016-09-28
      • 2011-07-28
      相关资源
      最近更新 更多