gpt4 book ai didi

ruby - 关于全局/范围的Ruby方法查找

转载 作者:数据小太阳 更新时间:2023-10-29 08:36:06 25 4
gpt4 key购买 nike

我试图全面了解ruby是如何定位方法/符号的,但当它涉及到多个级别时,尤其是全局/文件范围时,我就很难理解。
当显式调用类上的方法时,有很多关于搜索类及其包含的模块的顺序的说明(以及在每种情况下super调用的确切顺序)。但是,如果没有显式地调用方法,例如一个普通的func args而不是self.func args,那么搜索顺序是什么?
为什么在下面的示例中,调用func的成员方法在全局之前找到成员方法,而func2在没有调用method_missing的情况下找到全局?
如果全局是module/class/type,那么为什么没有找到同名的成员呢?
当语言在一种方法中遇到“funcfunc()func arg”等时,是否有正式的文档说明它具体做了什么?有很多第三方博客,但它们只讨论了include Moduleclass Type < BaseType的单个实例。

def func; "global func" end
def func2; "global func 2" end

class Test
def x; func end
def y; func2 end
def z; Math end
def w
func = "local_var"
[func(), func]
end


def func(arg=nil); "method func" end
def func=(x); puts "assign func=" end
def Math; "method Math" end

def method_missing(sym, *args, &block)
puts "method_missing #{sym}"
super(sym, *args, &block)
end
end

x = Test.new
puts x.x.inspect # "method func", member overrides global
puts x.y.inspect # "global func 2", "method_missing" was not called
puts x.z.inspect # "Math" module, member did not override global
puts x.w.inspect # ["method_func", "local_var"], local variables are always considered before anything else

最佳答案

Ruby的方法查找算法实际上非常简单:
检索接收器的class指针
如果方法存在,则调用它
否则检索superclass指针,并重复
就是这样。
如果算法到达一个不再有超类的点,但它仍然没有找到方法,它将再次重新启动整个过程,并将method_missing作为消息,原始消息的名称放在参数前面。但就是这样。这就是整个算法。它非常小而且非常简单,而且必须非常小而且非常简单,因为方法查找是面向对象语言中最常执行的操作。
注意:我完全忽略了Module#prepend/Module#prepend_features,因为我对它的工作原理知之甚少。我只知道它的作用,这对我来说已经足够了。
另请注意:我忽略性能优化,例如将方法查找的结果缓存在多态内联缓存中。
好吧,但这里有个诀窍:那些classsuperclass指针到底指向哪里?好吧,他们没有指出Object#classClass#superclass方法返回的结果。所以,让我们退一步。
每个对象都有一个指向对象类的指针。每个类都有一个指向其超类的指针。
让我们开始一个运行示例:

class Foo; end

现在,我们有了类 class,它的 superclass指针指向 Foo
foo = Foo.new

我们的对象的指针指向。
def foo.bar; end

现在事情开始变得有趣了。我们已经创建了一个单例方法。实际上,没有单例方法,它只是单例类中的一个普通方法。那么,这是怎么回事?好了,现在 superclass指针指向 Object的单例类,而 foo的单例类的 class指针指向 Foo!换句话说,singleton类被插入到 class和它的“real”类 foo之间。
但是,当我们询问其类时,它仍然会回答:
foo.class #=> Foo

foo方法知道singleton类,只需跳过它们,跟随 superclass指针,直到找到“normal”类,然后返回该类。
下一个并发症:
module Bar; end

class Foo
include Bar
end

这里发生了什么?ruby创建了一个新类(我们称之为include类)。这个类的方法表指针、类变量表指针和常量表指针指向 Foo的方法表、类变量表和常量表。然后,ruby使 fooFoo指针指向 foo的当前超类,然后使 Foo的超类指针指向 Object#class。换句话说,包含模块将创建一个新类,该类作为包含模块的类的父类插入。
这里有一点复杂:您还可以将 superclass模块转换成模块。这是怎么回事?好吧,ruby只是跟踪包含在模块中的模块。然后,当模块被包含到类中时,它将递归地为每个包含的模块重复上述步骤。
关于ruby方法查找,您只需要知道这些:
找到班级
跟随超类
单例类在对象上方插入
包含类在类上方插入
现在让我们看看你的一些问题:
当显式调用类上的方法时,有很多关于搜索类及其包含的模块的顺序的说明(以及在每种情况下 Barʹ调用的确切顺序)。但是,如果没有显式地调用方法,例如一个普通的 Bar而不是 Barʹ,那么搜索顺序是什么?
相同的。 superclass是隐式接收器,如果不指定接收器,则接收器为 Foo。括号是可选的。换句话说:
func args


self.func(args)

为什么在下面的示例中,调用 Foo的成员方法在全局之前找到成员方法,而 Barʹ在没有调用 include的情况下找到全局?
在ruby中没有“全局方法”这样的东西。也没有所谓的“成员方法”。每个方法都是实例方法。时期。没有全局、静态、类、单例、成员方法、过程、函数或子例程。
在顶层定义的方法成为类 superfunc args实例方法。 self.func args继承自 self。运行上面我概述的步骤,您会发现到底发生了什么:
检索 self的类指针: func
func2是否有一个名为 method_missing的方法:是,因此调用它。
现在又:
检索 private的类指针: Object
Test是否有一个名为 Object的方法:否!
检索 x的超类指针: Test
Test是否有一个名为 func的方法:是,因此调用它。
如果全局是module/class/type,那么为什么没有找到同名的成员呢?
再说一遍,这里没有全球性的,这里没有会员。这也与模块或类无关。而且ruby没有(静态)类型。
Math

是对常数的引用。如果你想调用一个同名的方法,你必须确保ruby能分辨出它是一个方法。只有方法才能拥有两件事:一个接收者和参数。因此,您可以添加接收器:
self.Math

或参数:
Math()

现在ruby知道你指的是方法而不是常量。
顺便说一下,这同样适用于局部变量。和二传手。如果你想调用setter而不是赋值局部变量,你需要说
self.func = 'setter method'

关于ruby - 关于全局/范围的Ruby方法查找,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39408538/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com