关于元编程
Wikipedia 上关于元编程的定义说元编程就是将程序作为数据进行处理。“用程序来处理程序”,这就是“元”的来源了,这本身是一个容易产生混淆的地方,就像“用语言来描述语言”一样,数学上的许多悖论就来自于此呢。幸好我们用的编程语言比自然语言要简单许多,并且都有严格的定义规范,有兴趣的人可以尝试在自己喜欢的编程语言里面构造一下 “This statement is false” 这个经典悖论。
回到元编程,程序处理程序可以分为“处理其他程序”和“处理自己”,对于前者,有我们熟悉的 lex 和 lacc 作为例子。而对于后者,如果再细分,可以分为“宏扩展”、“源代码生成”以及“运行时动态修改”等几种。
宏扩展从最简单的 C 语言的宏到复杂的 Lisp 的宏系统,甚至 C++ 的“模板元编程”也可以包含在这一类里面,我在这里对它们进行了一些介绍。
源代码生成则主要是利用编程语言的 eval
功能,对生成出来的源代码(除了在 Lisp 这样的语言里面以外,通常是以字符串的方式)进行求值。有一类有趣的程序 quine ,它们运行的结果就是把自己的源代码原封不动地打印出来,通常要证明你精通某一门语言,为它写一个 quine 是绝佳的选择了,这里有我搜集的一些相关资料。
最后是运行时修改,像 Lisp 、Python 以及 Ruby 之类的语言允许在运行时直接修改程序自身的结构,而不用再经过“生成源代码”这一层。当然对源代码进行 eval
的方法也许是最灵活的,但是它的效率却不怎么样,所以相来说并不是特别流行。这里主要介绍的是这种方式的元编程在 Ruby 里面的应用,如果对元编程的其他方面感兴趣,前面的几个链接应该都是很好的去处。
类与对象
Ruby 是一门完全面向对象的语言,在 Ruby 里面,所有的东西都是对象。对象是什么?对象是类的一个实例。那么类又是什么?按照前面的说法,一切都是对象,那么类也应该是一个对象喽!没错,类也是一个对象。按照前面的说法,对象应该是某一个类的一个实例,这也没错,一个特定的类就是“类(Class)”这个类的一个实例。已经开始拗口了吧?因为已经开始接触到“元”了,也就是所谓的用自己来描述自己,而描述一个类的类,通常叫做“元类(metaclass)”,不过在 Ruby 里面,元类和普通类其实是同一个东西。事实上, Ruby 还是一个单根的面向对象语言,所有的类都继承自 Object 类,包括 Class 类。运行一下下面的单元测试代码就可以验证这一点(完整的代码可以在末尾找到下载链接)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| require 'test/unit'
class MetaTest < Test::Unit::TestCase
def test_class
str = String.new
# str is an instance of the class String
assert_equal String, str.class
# the class String is an instance of the class Class
assert_equal Class, str.class.class
# the class Class is an instance of the class Class itself,
# so metaclass is just normal class
assert_equal Class, str.class.class.class
# the class Class is also derived from the class Object
assert_equal Object, str.class.class.superclass.superclass
end
end |
和其他面向对象语言一样。Ruby 里面对象保存成员变量的值,但是并不保存成员函数(方法),方法是放在对象所属的类里面。事实上,由于对象里面不会保存函数,Ruby 的底层实现里面类这种对象和普通对象还是有一定的区别的:
321
322
323
324
325
326
327
328
329
330
331
| struct RObject {
struct RBasic basic;
struct st_table *iv_tbl;
};
struct RClass {
struct RBasic basic;
struct st_table *iv_tbl;
struct st_table *m_tbl;
VALUE super;
}; |
只是我们在 Ruby 里面看起来它们是一样的,或者说,类的类型派生自对象的类型,加了一个放函数的表和一个指向父类的域。
另外,还有一类方法叫做“类方法”,它不能引用到对象的成员变量,例如:
class Foo
def Foo.func1
end
def Foo.func2
end
end
而在类的环境里面 self
是指向类自己的引用,因此也可以用 self
代替代码中的 Foo
,像这样:
另外,Ruby 还允许另外一种写法:
class Foo
class << self
def func1
end
def func2
end
end
end
事实上,在类方法里面不能引用到对象的成员变量的原因很简单,因为类方法的 self 引用到特定的类,而成员方法引用到特定的对象。看下面的测试代码:
def test_class_method
# foo is an instance of class Foo
foo = Foo.new
# klass is the class of the object foo, i.e. Foo
klass = foo.class
assert_equal Foo, klass
# class method of object foo is just instance method
# of object klass(which is the class of foo)
klass.set(10)
assert_equal 10, klass.get
# because get is not instance method of object foo
assert_raises(NoMethodError) { foo.get }
end
但是在 Foo
这个对象的类里面并不能找到 get
这个方法,如果说 get
是 Foo
的成员方法的话,那么它应该包含在 Foo.class.instance_methods
里面才对。不过这里其实另有乾坤,关系到一个 singleton class
,我们将在后面再回到这个问题上来。
Ruby 里面的元编程
一个比较常见的例子当属 define_method
。Ruby 里面有 attr_accessor
方法,例如:
class Foo
attr_accessor :foo, :bar
end
则 Foo
这个类就有了 foo
、foo=
、bar
、bar=
这些方法,用于读取和设置 foo
和 bar
两个属性。使用起来很方便,其实一点也不神奇,用 define_method
我们立马就可以写一个,由于 Ruby 的类可以随时重新打开添加东西,我们直接修改 Class
类:
class Class
def prop_accessor(*props)
props.each do |prop|
define_method prop do
instance_variable_get "@#{prop}"
end
define_method "#{prop}=" do |val|
instance_variable_set "@#{prop}", val
end
end
end
end
在定义 Foo
的时候 self
是引用到 Foo
(这是 Class
这个类的一个实例),因此可以使用 Class
类的成员方法,所以可以像前面的例子里面使用 attr_accessor
那样来使用这里定义的 prop_accessor
,效果是一样的。
对象真的不能持有自己的方法吗?
是的,对象不能持有自己的方法,在前面看到 Ruby 的实现代码里面 RObject
这个结构里面根本没有用于存放方法的表。但是我真的想让对象有它自己的方法,要怎么办呢?只有 RClass
结构能保存方法列表,于是就让 RObject
保存一个自己的 RClass
类就好了。这个类通常叫做“singleton class”,甚至也有叫“metaclass”的,但是通常我们认为“metaclass”是用于定义类的类,容易造成混淆,Matz 正在考虑在以后的版本里面把这个类叫做“eigenclass”,那么我们这里就称它为“eigenclass”好了。一个对象(例如 foo
)的 eigenclass 与它所属的类(可以通过 foo.class
获得)有几点不同:
- eigenclass 是对象所独有的,属于同一个类的不同对象拥有不同的 eigenclass 。
- Ruby 目前没有提供像
foo.class
这样的方法来直接获得这个 eigenclass ,不过幸运的是我们可以很轻松地实现这个方法。
- Ruby 会优先从 eigenclass 里面查找方法,其次才是对象所属的类以及各个祖先类。
虽然没有内置的轻松获取 eigenclass 的方法,但是将方法存放到 eigenclass 却是非常简单的,事实上语法我们已经很熟悉了。
def test_singleton_method
foo = Foo.new
# now we put the singleton_func in the eigenclass of foo
def foo.singleton_func
self
end
# singleton_func exists in foo, and self refer to foo
assert_equal foo, foo.singleton_func
# singleton_func is not available in other instance of class Foo
assert_raise(NoMethodError) { Foo.new.singleton_func }
end
另外,还可以这样写
class << foo
def singleton_func
self
end
end
是不是觉得似曾相识?没错,就是前面定义类方法的时候用的语法。可是那里点好前面是一个类,这里是一个对象,不是吗?没错!可是别忘了在 Ruby 里面,类也是一个对象!好了,让我们用 eigenclass 和 singleton_method 来解释一下前面的类方法的代码。
这是在 Foo
这个对象的 eigenclass 里面放入了 func1
这个方法。这也是我们在 Foo.class
里面找不到 func1
的原因了,因为它根本不在那里。现在我们已经迫不及待地想要取出 eigenclass 来看看了,怎么能容忍它偷偷地躲在那里呢?下面我们来给 Ruby 里面所有类的元祖 Object
类开开刀:
class Object
def eigenclass
class << self
self
end
end
end
这么简单几行代码就 OK 了,在 eigenclass
方法内,打开 self
(这里引用到当前的对象) 的 eigenclass ,打开以后里面那个 self
就引用到我们要的那个 eigenclass 了,注意最后一行是作为返回值的,这样我们就成功取出了 eigenclass ,赶紧测试一下!
def test_eigenclass
foo = Foo.new
def foo.singleton_func
self
end
# eigenclass is also an instance of the class Class
assert_equal Class, foo.eigenclass.class
# singleton_func is put as an instance method in the eigenclass
assert foo.eigenclass.instance_methods.include?('singleton_func')
# so here is the magic of the so called class method
assert Foo.eigenclass.instance_methods.include?('get')
end
回到类方法的问题上。我们进行如下定义以方便描述
foo = Foo.new
klass = foo.class
metaclass = klass.class
eigenclass = klass.eigenclass
foo
的成员方法很简单,它是存放在 klass
中的方法。也就是 klass
作为一个类所拥有的方法,调用 klass
的 instance_methods
就可以得到这些方法。
foo
的类方法,它是 klass
的成员方法,但是并不是像普通的成员方法那样存放在 metaclass
中,而是存放于 klass
的 singleton class eigenclass
中。
define_singleton_method
singleton method 并不是 Ruby 独有的,在 CLOS、Dylan 等语言里面也有,而像 Self 和 NewtonScript 之类的基于原型的语言甚至只有 singleton method 。Ruby 里面采用这种方式漂亮地实现了“类方法”。遗憾的是,目前的 Ruby 里面并没有提供一个和 define_method
同样功能的用于定义类方法的方法,我们只好自己写一个了。
class Object
def define_singleton_method name, &body
eigenclass.send :define_method, name, &body
end
end
由于 define_method
是私有方法,因此我们需要使用 send
来调用。或者也可以使用 instance_eval
的方式来实现。另外,在类里面使用的时候,我们可以用一个更明确的名字:
class Class
alias define_class_method define_singleton_method
end
Dwemthy’s Array 是一个很有趣的 Ruby 元编程的例子。仅用了几十行代码就写好了一个 RPG 游戏。在这里我们来把它的 Creature
类的 traits
类方法简化一下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class Creature
def self.traits(*arr)
return @traits if arr.empty?
attr_accessor *arr
arr.each do |trait|
define_class_method trait do |val|
@traits ||= { }
@traits[trait] = val
end
define_method :initialize do
self.class.traits.each do |k, v|
instance_variable_set("@#{k}", v)
end
end
end
end
end |
参考文献
附件
Testcase
分享到:
相关推荐
Ruby元编程是编程领域中一个深入且强大的主题,它允许程序员在运行时修改或创建代码,极大地提高了灵活性和代码的动态性。这本书“Ruby元编程第二版”专注于讲解Ruby语言的这一独特特性,旨在帮助开发者更好地理解和...
ruby元编程.pdf ruby 元编程 这本书对ruby的调用原理做了非常精辟的分析。 ruby 元编程 这本书对ruby的元编程方式做了比较好的阐释。 ruby 元编程 这本书对结对编程有一个很好的提现。
根据提供的文件信息,本文将对《Ruby元编程》这一主题进行深入探讨,解析其核心概念、应用场景以及为何元编程在Ruby语言中具有重要的地位。 ### 一、Ruby元编程简介 #### 1.1 元编程定义 元编程是指编写能够生成或...
Ruby元编程是编程的一种高级技巧,它允许程序员在运行时动态地修改或创建代码,极大地提高了灵活性和代码的可扩展性。Ruby作为一种动态类型语言,其元编程能力尤为强大,使得开发者可以创建出高度定制化的解决方案。...
通过分析案例、讲解例题、回顾Ruby代码库的实现细节,作者不仅向读者展示了Ruby编程的优势和Ruby特有的解决问题的方式,更详细开列出发挥其优势的技巧和常用的Ruby设计模式。Ruby之父松本行弘作 序推荐。
在Ruby这种动态编程语言中,元编程是一种核心技术,它不仅仅是一种技巧,更是Ruby和Rails框架中用于提高开发效率和软件复用性的关键方法。 在Ruby语言中,元编程主要通过方法拦截、动态方法定义、元类以及一些特殊...
Ruby元编程中文版
本书的第一部分“Ruby元编程”是本书的核心部分,他讲述了一个资深程序员在办公室一周发生的事情。 本书的第二部分“Rails中的元编程”是一个元编程实例,Rail是Ruby标志性框架。 在阅读本书之前,需要了解本书的三...
Ruby元编程是编程领域中的一个高级技术,它允许程序员在运行时操纵语言的结构和行为。这本《Metaprogramming Ruby》书籍深入探讨了如何利用Ruby的特性进行元编程,帮助开发者提升代码的灵活性、可扩展性和复用性。源...
ruby元编程 第2版 中文 目录清晰 方便广大通勤族路上看,如果喜欢请购买正版纸质图书
### Ruby元编程核心知识点概述 #### 一、元编程概念及意义 元编程是一种编程技术,它允许程序在运行时修改或生成其他程序的行为。在《Ruby元编程》这本书中,作者通过一系列案例来深入浅出地介绍了Ruby语言中独特...
- **Kosmas Chatzimichalis**(软件工程师):本书不仅深入讲解了Ruby及其框架Ruby on Rails的核心机制,还提供了一系列宝贵的工具和技巧,帮助读者将Ruby编程水平提升到一个新的高度。 - **Arialdo Martini**...
《Ruby元编程》以案例形式循序渐进地介绍了Ruby特有的实用编程技巧 元编程 通过分析案例 讲解例题 回顾Ruby代码库的实现细节 《Ruby元编程》作者Paolo Perrotta不仅向读者展示了Ruby编程的优势和Ruby特有的解决问题...
本资料包主要关注的是基于Ruby编程的基础知识和应用,包括如何使用Ruby进行开发和解决问题。 Ruby的设计哲学强调程序员的生产力和代码的可读性。它的语法直观,使得初学者能够快速上手。Ruby的核心特性包括: 1. ...
Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门...
### Ruby编程语言简介 #### 一、Ruby编程语言概述 Ruby是一种动态的、面向对象的、通用型的编程语言,自1995年由日本程序员松本行弘(Yukihiro Matsumoto)创建以来,便以其简洁优雅的语法、高度可读性和易于学习...
# 编程之魂:Ruby中的元编程艺术 Ruby 是一种设计精良的高级编程语言,自1995年由日本开发者松本行弘(Yukihiro "Matz" Matsumoto)创建以来,就因其优雅简洁的语法、强大的功能以及高度的灵活性而受到众多程序员的...