5.1 定义
Next: Inheritance,Previous: Object-oriented programming,Up: Object-oriented programming
5.1 定义
和一般的完整面向对象系统不一样的是,R 有一个类系统和基于类对象的分发机制。为解释型代码设置的分发机制依赖于求值框架下的四个特殊对象。它们分别是 .Generic,.Class,.Method和.Group。还有一个用于内部函数和类型的独立分发机制,但不是这里讨论的内容。
class 属性使类系统变得更加容易。该属性是类名字的列表。为创建类"foo"的一个对象,可以简单地把字符串"foo"赋给一个对象的类属性。事实上,任何东西都可成为类 "foo" 的一个对象。
对象系统通过两个分发函数,UseMethod 和 NextMethod,来使用泛型函数(generic functions)。对象系统的典型应用是从调用泛型函数开始的。通常,函数非常的简单,只有一行代码。系统函数 mean就是这样的一个函数,
> mean
function (x, ...)
UseMethod("mean")
在调用mean的时候,可以给定任意多的参数,但是它的第一个参数是特定的并且第一个参数的类用来决定调用那一种方法。变量 .Class 设为 x 的类属性,.Generic 设为字符串"mean",然后寻找正确的方法来执行。mean的任何其它参数的类属性都会被忽略。
假定 x 有一个依次含有 "foo"和"bar" 的类属性列表。 R 可能首先搜索名为 mean.foo 的函数,如果该函数找不到,它可能随后去找名为 mean.bar 的函数,此时如果还不能成功,则最后寻找函数 mean.default。如果最后的搜索仍不能成功, R 将会报错。因此,记得编写默认的方法是一个好习惯。注意,这里提到的mean.foo 等函数指的是方法。
NextMethod 还提供里另外一种分发机制。函数可以在其内部任何地方调用 NextMethod。决定哪个方法会被调用主要基于 .Class 和 .Generic 的当前值。这里稍稍有点问题,因为方法实际上就是普通的函数,那么用户就可能直接调用它。如果他们这样做,那 .Generic 或 .Class 就没有赋值了。
如果一个方法被直接调用,它就包括一个对 NextMethod 的调用,并且 NextMethod 的第一个参数用于决定泛型函数。如果这个参数没有提供,系统会报错;因此最好记得提供该参数。
在方法直接调用的时候,方法的第一个参数的类属性作为 .Class 的值。
方法自身利用 NextMethod 提供继承形态。通常,一个特定方法用一些操作启动数据然后根据 NextMethod 调用下一个适当的方法。
考虑一下下面这个简单的例子。二维欧氏空间里面的一个点可以通过笛卡尔坐标或极坐标(r-θ)确定。因此,为存储点的位置信息,我们可以定义两个类,"xypoint"和 "rthetapoint"。 所有 `xypoint' 数据结构由一个x-分量和一个y-分量构成的列表组成。所有 `rthetapoint' 对象由一个r-分量和一个theta-分量构成的列表组成。
现在,假定我们想得到任一对象的x-位置坐标。通过泛型函数,这非常容易实现。我们如下定义了一个泛型函数 xpos。
xpos <- function(x, ...)
UseMethod("xpos")
现在我们可以定义方法:
xpos.xypoint <- function(x) x$x
xpos.rthetapoint <- function(x) x$r * cos(x$theta)
用户可以简单地用任一描述方法作为参数调用函数 xpos。内在的分发方法判断对象的类,然后调用恰当的方法。
这使得非常容易加入其它坐标表述方式。我们没有必要写一个新的泛型函数,我们只要写新的方法就可以了。因此,对一个已经存在的系统非常容易扩展的,因为用户只负责处理新的表述方式而不用考虑已有的表述方式。
使用这种思路的一个例子是为不同类型的对象提供专门的打印输出;现在已经为 print 设计了40种方法。
Hits:Loading...
