有人能解释一下这段代码是如何工作的吗?

4

以下是一些代码:

def func
   def func
      1
   end
end

接着我在irb中尝试了以下操作:

func
func.func
func

并获取结果:

:func
1
1

能否有人解释一下发生了什么?我有点理解第一个输出,但不理解后面的。谢谢!

2个回答

5

在全局作用域中,您可以在方法内部定义方法。方法定义返回一个带有其名称的符号。

  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'

1
我正在尝试在func上调用a,但它返回了private method 'a' called for :func:Symbol (NoMethodError),Ruby 2.6.1p33 [x86_64-darwin18]。 - Sebastián Palma
1
这次请使用Ruby 2.5.5p157,确保与您答案中的完全相同 https://repl.it/@vnhnhm/PureBlaringSymbols。 - Sebastián Palma
在我的 ruby 2.5.1p57 上可以运行。请在新控制台中尝试。 - mrzasa
1
尝试使用Ruby 2.5.1p57(2018-03-29修订版63029)[x86_64-darwin18],出现相同的错误。 - Sebastián Palma
2
@sofi 关键在于,如果您在irb全局命名空间中定义一个方法,它将被添加到Kernel中,而Kernel几乎包含了所有Ruby对象。只需在全局命名空间中定义a方法,即可使1.a:foo.a"foo".a正常工作。当调用foo.foo时,您只需在第一个foo调用的返回值上调用foo即可。 - 3limin4t0r
显示剩余6条评论

4

这很复杂。在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 的一种有意设计,而是为了应对奇怪情况的后备方案。主设计师 不喜欢它甚至被可能性本身所激怒。

...嵌套方法定义的当前行为是无用的。它应该被废除以打开未来的可能性(我会投票支持警告)。

你的代码表现得如此奇怪,是因为你在顶层执行,此时 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接