R语言OOP(1):基础

来源:互联网 发布:windows查看本机端口 编辑:程序博客网 时间:2024/06/06 03:40

OOP(面向对象程序设计)使软件编写和维护容易了,程序可靠性也提高,但程序的易用性降低,对用户素质的要求高了很多。所以,OOP不只是程序员的事情,使用者也多少得有些了解,尤其是R的使用者。

以Bioconductor为例,当前版本(2013.9.23同步的2.13版)中定义S4类有13190个,而函数则多大106939个。有些软件包从专用文件读取数据直接生成类对象,数据的结构被包装得严严实实的,用户根本看不见里面的实际东西;如果没使用指定的实验仪器获得相应的数据文件,不了解数据结构和OOP的方法就根本没办法应用这些软件。学OOP,是用好R应用软件包的又一个门槛,跨吧。

1 对象和类

1.1 对象和类

“对象”,英文是Object,物体,就是很具体的一个物件。推而广之,只要是能对它做点什么的东西都叫对象。

“类”(CLASS),有共同特性的一组对象,但在编程中要反过来,指的是对象共有的那些特性。在OOP语言中,所有对象都属于某个或某些类。

1.2 继承

鼠娃继承鼠爸的特性,会打洞,这是遗传性继承。然而编程中的继承更像进化上的继承,是在“类”的继承,不是“对象”的继承。比如Biostrings定义的类XString,它有XRaw等4个父/母类和BString等4个子类:

library(RBioinf)library(Biostrings)superClassNames("XString")
## [1] "XRaw"      "XVector"   "Vector"    "Annotated"
subClassNames("XString")
## [1] "BString"   "DNAString" "RNAString" "AAString"

1.3 类的层次

R语言中S3 OOP的类层次很简单,因为“类”是存在class属性的一个向量,向量是线性的,所以类的层次也是线性的,越靠后“关系”越疏远。这种关系直接影响到泛型函数对方法的选择。

而S4 OOP由于允许多重继承(一个类可以有多个父/母类),所以层次关系非常复杂,一个类转来转去搞不好自己的祖父又成了自己的孙子。下面是Biostring定义的类的网络,算法从XString类开始,上下只延伸了4层关系就已经很复杂了(蓝色为没有父/子类,绿色为只有一个父/子类,黄-橙-红表示父/子类数量递增):



一个Biostrings包就这样了,如果把Bioconductor所有包中定义的S4类的网络关系做出来,那简直惨不忍睹。

2 方法、泛型函数和多态性

2.1 方法

方法是处理对象的动作,在R语言里面就是指函数,比如获取矩阵数据概貌的函数summary.matrix和获取因子型数据概貌的函数summary.factor:

x1 <- matrix(rep(1:3, each = 3), ncol = 3)x2 <- as.factor(x1)summary.matrix(x1)
##        V1          V2          V3   ##  Min.   :1   Min.   :2   Min.   :3  ##  1st Qu.:1   1st Qu.:2   1st Qu.:3  ##  Median :1   Median :2   Median :3  ##  Mean   :1   Mean   :2   Mean   :3  ##  3rd Qu.:1   3rd Qu.:2   3rd Qu.:3  ##  Max.   :1   Max.   :2   Max.   :3
summary.factor(x2)
## 1 2 3 ## 3 3 3

2.2 泛型函数

让用户在使用过程中根据数据类型选择函数是很麻烦也很容易出错的,最好把这工作交给程序自动处理。这个过程可以通过泛型函数实现。获取数据的概貌的泛型函数是summary,由它根据数据的类型来选择合适的方法,而用户只需要使用泛型函数:

summary(x1)
##        V1          V2          V3   ##  Min.   :1   Min.   :2   Min.   :3  ##  1st Qu.:1   1st Qu.:2   1st Qu.:3  ##  Median :1   Median :2   Median :3  ##  Mean   :1   Mean   :2   Mean   :3  ##  3rd Qu.:1   3rd Qu.:2   3rd Qu.:3  ##  Max.   :1   Max.   :2   Max.   :3
summary(x2)
## 1 2 3 ## 3 3 3

上面的summary是S3 OOP中的泛型函数,S4的泛型函数在细节上跟它有很大差异,这里先了解概念。

2.3 多态性

泛型函数根据数据的“类”来选择方法。由于数据类型很多,方法也会不少,比如summary泛型函数能够支配的方法包括:

## 代码的运行结果会根据载入的软件包不同而有差异methods("summary")
##  [1] summary.aov             summary.aovlist        ##  [3] summary.aspell*         summary.character      ##  [5] summary.connection      summary.data.frame     ##  [7] summary.Date            summary.default        ##  [9] summary.ecdf*           summary.factor         ## [11] summary.glm             summary.infl           ## [13] summary.lm              summary.loess*         ## [15] summary.manova          summary.matrix         ## [17] summary.mlm             summary.network        ## [19] summary.nls*            summary.packageStatus* ## [21] summary.PDF_Dictionary* summary.PDF_Stream*    ## [23] summary.POSIXct         summary.POSIXlt        ## [25] summary.ppr*            summary.prcomp*        ## [27] summary.princomp*       summary.proc_time      ## [29] summary.Rle             summary.srcfile        ## [31] summary.srcref          summary.stepfun        ## [33] summary.stl*            summary.table          ## [35] summary.tukeysmooth*   ## ##    Non-visible functions are asterisked

上面体现的就是方法的“多态性”,相似的summary功能由不同的方法来实现。

2.4 方法的调度/派遣/分发(dispatch)

泛型函数在方法选择过程中所做的事情是比较复杂的,S3和S4差别很大,这将在以后学习。

3 获取类和方法的帮助信息

由于S3 OOP系统相对简单,R语言提供的用于S3类和方法检索的手段相当少。目前没有办法知道一个软件包中定义了哪些S3类,只能通过对象查询它所属的类属性;方法的检索使用methods函数,可以查询类或泛型函数的方法:

class(airquality)
## [1] "data.frame"
methods("plot")
##  [1] plot.acf*            plot.data.frame*     plot.decomposed.ts* ##  [4] plot.default         plot.dendrogram*     plot.density        ##  [7] plot.ecdf            plot.factor*         plot.formula*       ## [10] plot.function        plot.hclust*         plot.histogram*     ## [13] plot.HoltWinters*    plot.isoreg*         plot.lm             ## [16] plot.medpolish*      plot.mlm             plot.network        ## [19] plot.network.default plot.ppr*            plot.prcomp*        ## [22] plot.princomp*       plot.profile.nls*    plot.spec           ## [25] plot.stepfun         plot.stl*            plot.table*         ## [28] plot.ts              plot.tskernel*       plot.TukeyHSD       ## ##    Non-visible functions are asterisked
methods(class = "glm")
##  [1] add1.glm*           anova.glm           confint.glm*       ##  [4] cooks.distance.glm* deviance.glm*       drop1.glm*         ##  [7] effects.glm*        extractAIC.glm*     family.glm*        ## [10] formula.glm*        influence.glm*      logLik.glm*        ## [13] model.frame.glm     nobs.glm*           predict.glm        ## [16] print.glm           residuals.glm       rstandard.glm      ## [19] rstudent.glm        summary.glm         vcov.glm*          ## [22] weights.glm*       ## ##    Non-visible functions are asterisked

使用methods函数检索泛型函数可用“方法”的方式是非常原始的,它仅根据函数名的组成进行判断。如果养成了下面这种函数命名习惯,不知道什么时候就可能会出问题:

plot.xxx <- function(x) return(NULL)x <- methods("plot")x[grep("xxx", x)]
## [1] plot.xxx

S4的类定义比S3正式多了,所以获取类相关信息也较容易,工具函数相对多。如果知道类名称(使用class函数获取对象的类),可以使用showClass/getClass显示类的信息。一些软件包比如RBioinfo还提供了superClassNames和subClassNames等函数供使用(前面有例子)。

x <- DNAString("ATCGCC")class(x)
## [1] "DNAString"## attr(,"package")## [1] "Biostrings"
showClass("DNAString")
## Class "DNAString" [package "Biostrings"]## ## Slots:##                                                                       ## Name:           shared          offset          length elementMetadata## Class:       SharedRaw         integer         integer DataTableORNULL##                       ## Name:         metadata## Class:            list## ## Extends: ## Class "XString", directly## Class "XRaw", by class "XString", distance 2## Class "XVector", by class "XString", distance 3## Class "Vector", by class "XString", distance 4## Class "Annotated", by class "XString", distance 5

考虑兼容性问题,S4之前定义的一些类现在也被用类似S4的方法进行了处理,所以完全可用使用S4的方式获取S3类或隐含类的信息:

showClass("data.frame")
## Class "data.frame" [package "methods"]## ## Slots:##                                                                   ## Name:                .Data               names           row.names## Class:                list           character data.frameRowLabels##                           ## Name:             .S3Class## Class:           character## ## Extends: ## Class "list", from data part## Class "oldClass", directly## Class "vector", by class "list", distance 2
showClass("matrix")
## Class "matrix" [package "methods"]## ## No Slots, prototype of class "matrix"## ## Extends: ## Class "array", directly## Class "structure", by class "array", distance 2## Class "vector", by class "array", distance 3, with explicit coerce## Class "vectorORfactor", by class "vector", distance 4, with explicit coerce## ## Known Subclasses: "mts"

获取S4泛型函数可用方法的函数是getMethods/showMethods。

4 源代码获取和学习软件

BioC全部软件包的源代码可以在Linux下通过 rsync 命令获得,也可以从BioC的HTTP站点一个个下载。学习源代码还是推荐使用Emacs+etags/rtags。R和Emacs有很多相似的地方,如:两者都有非常出色的可扩展性,程序作者多数也都是程序的最终用户等;但两者的学习曲线都相当复杂,同时学这两个工具对谁都是一个很大挑战。


Author: ZGUANG@LZU

Created: 2013-09-26 四 20:51

Emacs 24.3.2 (Org mode 8.0.5)

Validate XHTML 1.0

0 0
原创粉丝点击