Matt Aimonetti 刚贴了篇文章元编程速度初探来讲述定义方法的不同对速度造成影响。他发现使用 define_method 定义工具``方法``的运行速度比静态定义的方法(例如使用``def method_name 定义的方法)``慢得多
。但是,文章后续报道:Matt 找到了原因和解决方案中关于速度的结论又有不同。速度不同的原因是:
今天 Wycats 告诉我原因在于 define_method:class_eval 的效率和常规代码一样高,和常规的 Ruby 代码一样,它也是在 eval.c 中被计算的;而 define_method 则需要编译 proc。
Matt 将上面的性能评测程序使用 class_eval 进行修改,给出了此程序的更新版本,这个版本的性能与静态定义方法的程序性能已经没什么不同。对于元编程工作来说,这是一个非常有用的经验。 元编程领域的另一个极端是 Reginald Braithwaite 的重写gem 系 列。重写gem 采用来自于LISP 或Scheme 的,被称为宏或宏展开的方法。对于Ruby 来说,这种方法需要稍微跨出Ruby 语言——常规Ruby 代码 并不允许访问已经被载入的Ruby 代码——反射止步于方法这一层次。在MRI 1.8 中,使用ParseTree 扩展来访问代码(从Ruby 解析器中得到抽象语法树(AST))。Rubinius 提供对ParseTree 的s- exprs 形式的支持(例如调试器支持对于方法的s-exprs 进行访问)。对于JRuby 也有一个不完整的Parse Tree 版本(它不支持对于特定方法类型的抽象语法树的访问)。ParseTree 并不支持Ruby 1.9。
重写gem 中具体的方法常用来解决特定的问题,但其中的原理的应用将更加广泛。现在需要解决的问题是向类中添加方法(开放类),这是个系统问题。例如有一段代码,作为一个方法foo 加入到Object 中,这个方法在运行时对于所有代码都是可见的——这会导致一些诸如命名冲突的问题(如果有另一个同名方法已经 存在于这个类中)。
重写gem 的解决方法是:将此新添加的方法的可见性限制于一个块中,例如这样:
<pre id="pocx23">with(andand) do<br id="pocx24"></br> foo().andand.bar(blitz())<br id="pocx25"></br>end
使用 with 语句处理的在块中的语句被转换为 ParseTree 的 s-exprs 形式,继而被分析及重写。在这种情况下,在其他地方并没有一个叫做andan``d
的方法——在块中,with 语句调用并重写这个方法,从而得到期待的行为。需要记住的是,重写 gem 中的实现只是以一种方式,还有更多的方法可以让我们在 Ruby 中体验宏的魅力。尽管 Ruby 关于块的便捷的语法可以容易地做到精确的标记,但传入太多的需要延迟评估的代码会使情况变得很复杂:这需要更多的冗长的方法创建 Proc。
对于像宏这样的概念的介绍,"Common Lisp 实践"是个不错的开始。还有一些项目应用ParseTree 以分析代码—— InfoQ 展示 Ambition、Sequel 和 merb 如何使用 ParseTree 。另外请参考 Joel Klein 的讨论——涉及延迟评估 lambda 算子部分,这里介绍了宏和 Lambda 演算。
最后,对于不熟悉用 Rudy 进行元编程的人, Dave Thomas(PragDave)的这一系列视频教程介绍了相关概念,本系列视频教程已经获得了一些正面的评价。
查看英文原文: Metaprogramming Roundup: Speed, Ruby Macros, Screencasts
评论