Ruby 分析 Ruby 代码的执行环境

sefier · 2013年09月25日 · 最后由 Fisher 回复于 2019年03月30日 · 5197 次阅读

最近阅读《Ruby 元编程》收益良多,对 Ruby 代码的执行,在理论上得到了更深的认识,这里,粗略地总结一下 Ruby 代码的执行环境。

这个总结只是初步的一些结论,许多特殊情况或者例外,可能被我忽略了,而造成了结论其实不正确,如果您发现了,不妨指出;另外,一些术语是我自己总结的,不一定符合惯例。

以下是正文。


什么是 Ruby 代码的执行环境?Ruby 是一种比较严格的单步解释型语言,每一行代码的执行,在不同的环境下结果显然不同,例如puts a,这个 a 存在么?是什么?导致的结果显然不一样,一般把这种环境称之为 binding。我认为,完整的执行环境,包括两大类五个方面:

第一类:scope,它包括 1)当前对象,也就是 self 所指代的对象,决定了在不指明 receiver 的情况下,默认的接受者,也决定了实例变量(就是 @打头的变量)存在于那个对象之中 2)局部变量池(我自己发明的),就是已经存在的局部变量及其值

第二类:level,它包括 1)当前类,也就是用 def 定义方法时,这个方法作为哪个类的实例方法? 2)常量所属的目录(要把常量理解为目录和文件一样的东西,类和模块是目录同时也是文件,其他普通常量就是文件) 3)类变量(也就是 @@打头的变量)所属的类,也就是使用类变量时,理解为哪个类?

下面的问题是,如何分析一段代码的上述 5 个方面?这个需要分为两个步骤:一个是初始状态,另外一个是这种状态如何改变。

初始状态,也就是 Ruby 代码最开始执行时,没有切换 scope 和 level 时,5 个方面的初始状况,分别是: 1)当前对象是一个名为 “main” 的 Object 实例对象,这个 main 只是一个普通的名称而已,其实就是实例化 Object 之后,覆盖了 to_s 方法返回 “main” 2)局部变量池为空 3)当前类是 Object 4)当前常量目录是:: 5)当前类变量所属的类是 Object

了解了初始状态后,我们就知道,在初始状态执行代码的环境了。那么,scope 和 level 是如何变化的呢? 1.在使用 module,class,def 关键字定义模块,类,方法时(以及用 end 退出时),切换 scope,具体的规则是: 在 module/clsss 定义处,切换 scope,当前对象是定义的模块或类本身,当前变量池为空 在 def 定义处,切换 scope,调用该方法的 receiver 为当前对象,当前变量池为空

当遇到 end 时,对应的切换结束,切换回切换之前的 scope。

2.在使用 module,class 关键字定义模块和类时,切换 level,规则很简单,目录是递进式切换,从根目录进入 A 的 module,目录就是::A,再进入里面的类 B,则目录是::A::B,当前类以及当前类变量所属的类,均是定义的类。

了解了初始状态,也了解了状态的变化,那么我们就可以完整地分析代码的执行环境了。但是,我们为什么要分为 5 个方面呢?比如 scope 的两个方面,切换时都切换了,作为一个方面分析不是更简单吗?这是因为,ruby 提供了一些手段,使得我们只切换 scope 中的一些内容(比如只更改 self,也有人称之为 flat scope,认为没有切换 scope)。也就是说,有一些特殊手法,可以按照需要,只切换其中一些方面,而保留另外一些方面,这就比我们上面所说的切换方法灵活多了。具体来说:

一、任何对象均可以调用一个名为 instance_eval 的方法,提供一个 block,有两方面发生变化: 1)这个 block 里面的当前对象,也就是 self 所指的对象,临时地脱离当前 scope 所指的对象,而变为调用此方法的对象 2)这个 block 里面定义的方法,临时脱离当前类,而是这个对象的 eigenclass

二、任何模块对象(也就是 Module 及其子类(主要是 Class)的实例)均可以调用一个一个名为 class_eval 的方法,提供一个 block,有以下变化: 1)这个 block 里面的当前对象为该模块 2)当前类也是该模块。(但是并不影响其他内容,尤其是,在这个 block 定义的类变量不属于该类,定义的常量,也不会置于这个类代表的目录之下,这就说明了要把 level 分为 3 个方面分析的重要性) 值得注意的是:通过 Class.new 执行的 block,其实在内部是使用了 class_eval 来执行,所以,规则同 class_eval 相同。

三、class << object 定义,会有以下变化: 1)切换 scope,当前对象为 eigenclass,当前作用域为新作用域 2)当前类为 eigenclass(但是当前目录和当前类对象均维持不变)

除此之外,block 并非 gate(既不是 scope gate,也不是 level gate),所以不会切换任何 scope 和 level,维持原先的 5 个方面状态不变。


最后出一个小小的谜题,便于你理解 ruby 执行代码的方式:

puts = 5
puts puts

上述代码是合法的吗?如果合法,结果是什么?(反之,错误在哪?)

这个帖子怎么没人回复?

头像上的号码很亮啊

@ShiningRay 看到你留言, 我特意点开头像看了下, 有 10 位数

这个 name 可以重名?

尽量别写 puts puts 这种没有实际意义的代码. 就好象子类继承父类,父类又继承子类,感觉是乱伦.

#5 楼 @fsword 难道真的不是你么…

哈哈哈哈,太逗乐了,我这个 name 是随便写的,貌似是抄的桃李福,就是为了测试是不是可以重名。

照片请忽略,含有不健康内容。。。。

#7 楼 @hooopo 确实不是,马甲不是我的风格,是别人引我看到这贴的,没想到倒象马甲顶贴了,呵呵

最近有没有做兼职或者跳槽的打算呀

总结得好,赞

sefier Ruby 类的问题 提及了此话题。 07月17日 11:19

老铁接外包业务不

最近也在研究这个,结合《Ruby 原理剖析》这本书看,会更加恍然大悟

需要 登录 后方可回复, 如果你还没有账号请 注册新账号