R语言OOP(3):S4的实现方法
来源:互联网 发布:windows查看本机端口 编辑:程序博客网 时间:2024/06/05 14:45
搁置了一些时日,原本写好的提纲很多都忘了要写些什么内容了。一些标题先留空,以后有时间再补充吧。
1 闲话
《R Language Definition》中只有S3 OOP的介绍,找不到S4 OOP的相关说明,因为S4 OOP还不够完善,没有定型。但很多人已经大量使用它了,尤其是BioC们,前些年用S3写的一些包都在逐渐更新为S4系统,他们可能是尝到了某些甜头。不过苦头肯定也不少,R core们宣布自R 3.0版后弃用类定义setClass函数的representation参数,改用slots和contains参数。BioC们又有得折腾了。
话虽如此,好的系统还是很有生命力的,能吸引用户。同样是序列处理的软件包,seqinr出现的时间也不短了,没用S4,和BioC格格不入,功能虽然很齐全但响应者似乎寥寥。我看seqinr说明书的第一感觉就一个字:“乱”,心情也是一个字:“烦”。不过应该肯定的是seqinr还是非常不错的,小巧、函数间的依赖关系简单、容易移植,适合小量序列的分析。闲话少说,入题。
2 S4类
2.1 S4类定义
S4 OOP系统有比较完善的CLASS定义方法,用于类定义的函数是setClass。
## NOT RUN (非运行代码)setClass(Class, representation, prototype, contains = character(), validity, access, where, version, sealed, package, S3methods = FALSE, slots)
2.1.1 最基本的设置:Class和slots
先看例子再解释:
Somebody <- setClass(Class = "Somebody", slots = c(name = "character", gender = "factor"))someone <- Somebody(name = "Adam", gender = factor("M"))someone
## An object of class "Somebody"## Slot "name":## [1] "Adam"## ## Slot "gender":## [1] M## Levels: M
上面第一行代码定义了一个类的最基本两个内容:类名称和类对象存储的数据。参数说明如下:
- Class 用于表示类名称的字符串
- slots 很多人称它为“接口”,用于存储对象的具体数据。既然是数据就得有名称和数据类型,slots的作用就是指定这两者。它的内容是有名的列表或有名向量,列表或向量的名称(如上面的name和gender)表示类数据的名称,而它们的值(字符串)表示数据的类型(如上面的character和factor)。
setClass函数结果可以不赋值,但赋值运算会在创建类的同时获得一个类构造函数。构造函数的名称一般和类名称相同,但这不是硬性规定:
diets <- setClass(Class = "Diet", slots = c(food = "character", drink = "character"))diets(food = "rice", drink = "water")
## An object of class "Diet"## Slot "food":## [1] "rice"## ## Slot "drink":## [1] "water"
如果setClass函数结果不赋值,构造函数不会产生,如果要产生此类对象就得使用系统的new函数了。
setClass(Class = "Anybody", slots = c(name = "character", gender = "factor"))
## 错,类定义并没有赋值,没有产生Anybody构造函数Anybody(name = "Adam", gender = factor("M"))
## Error: 没有"Anybody"这个函数
new(Class = "Anybody", name = "Adam", gender = factor("M"))
## An object of class "Anybody"## Slot "name":## [1] "Adam"## ## Slot "gender":## [1] M## Levels: M
## setClass赋值的作用事实上就是产生了一个调用new函数的函数:Somebody
## class generator function for class "Somebody" from package '.GlobalEnv'## function (...) ## new("Somebody", ...)
从R语言的根本上来看(不涉及任何OOP概念),slots和CLASS一样,都是数据的属性而已,只是换了个名称:
str(attributes(someone))
## List of 3## $ name : chr "Adam"## $ gender: Factor w/ 1 level "M": 1## $ class : atomic [1:1] Somebody## ..- attr(*, "package")= chr ".GlobalEnv"
2.1.2 类继承:contains
- contains 用于设置新类中包含(即继承)的其他已定义类(父类),其作用是把父类中的slots包含进来:
showClass("Somebody")
## Class "Somebody" [in ".GlobalEnv"]## ## Slots:## ## Name: name gender## Class: character factor
Anybody <- setClass("Anybody", contains = "Somebody", slots = c(skin = "character"))showClass("Anybody")
## Class "Anybody" [in ".GlobalEnv"]## ## Slots:## ## Name: skin name gender## Class: character character factor## ## Extends: "Somebody"
contains可以包含多个类,继承多个父类的slots:
Anybody <- setClass("Anybody", contains = c("Somebody", "Diet"), slots = c(skin = "character"))showClass("Anybody")
## Class "Anybody" [in ".GlobalEnv"]## ## Slots:## ## Name: skin name gender food drink## Class: character character factor character character## ## Extends: "Somebody", "Diet"
2.1.3 原型设置:prototype
- prototype 原型,即类对象初始化(新建对象)时slots中的默认数据,相当于函数的默认参数。这是一个有名列表,不能用有名向量。
Somebody <- setClass(Class = "Somebody", slots = c(name = "character", gender = "factor"), prototype = list(name = "Adam", gender = factor("M")))str(Somebody())
## Formal class 'Somebody' [package ".GlobalEnv"] with 2 slots## ..@ name : chr "Adam"## ..@ gender: Factor w/ 1 level "M": 1
diets <- setClass(Class = "Diet", slots = c(food = "character", drink = "character"), prototype = list(food = "rice", drink = "water"))str(diets())
## Formal class 'Diet' [package ".GlobalEnv"] with 2 slots## ..@ food : chr "rice"## ..@ drink: chr "water"
创建新类时如果使用contains,新类的构造函数将自动父类的原型设置
Anybody <- setClass("Anybody", contains = c("Somebody", "Diet"), slots = c(skin = "character"), prototype = list(skin = character()))str(Anybody())
## Formal class 'Anybody' [package ".GlobalEnv"] with 5 slots## ..@ skin : chr(0) ## ..@ name : chr "Adam"## ..@ gender: Factor w/ 1 level "M": 1## ..@ food : chr "rice"## ..@ drink : chr "water"
2.1.4 类型检查:validity
定义S4新类时本身就已经具备类型检查的能力,如果给对象提供的数据类型不正确就直接出错而不是警告:
## 下面的skin不是字符型,不能通过类型检查Anybody(skin = 1)
## Error: invalid class "Anybody" object: invalid object for slot "skin" in## class "Anybody": got class "numeric", should be or extend class## "character"
然而,这种检查能力还是相当有限的,编程过程中往往需要对数据进行更严格的审查,比如对数据进行进行数量和取值的限定。这可以通过validity参数设置类型检查函数来实现。
Anybody <- setClass("Anybody", contains = c("Somebody", "Diet"), slots = c(skin = "character"), prototype = list(skin = character()), validity = function(object) { if (length(object@skin) != 1) return("\"skin\" must be length of 1.") if (!object@skin %in% c("W", "Y", "B")) return("Invalid \"skin\" type.") return(TRUE) })
## 正确设置可以通过类型检查(xx <- Anybody(skin = "Y"))
## An object of class "Anybody"## Slot "skin":## [1] "Y"## ## Slot "name":## [1] "Adam"## ## Slot "gender":## [1] M## Levels: M## ## Slot "food":## [1] "rice"## ## Slot "drink":## [1] "water"
## validity设置了skin只能是‘W’,‘Y’,‘B’中的一个,设置其他值或长度不为1都出错Anybody(skin = c("Y", "W"))
## Error: invalid class "Anybody" object: "skin" must be length of 1.
Anybody(skin = "Black")
## Error: invalid class "Anybody" object: Invalid "skin" type.
2.1.5 setClass函数的其他参数
除以上参数外,类定义函数setClass还可以设置的参数有:
- where 用于指定类定义存储的环境(命名空间),如果在软件包里面定义类,类默认存储到软件包的命名空间
- package 此参数极少使用,作用和where类似
- sealed 是否封装(TRUE/FALSE)。如果设为TRUE,已经用setClass定义过的类(名称)就不能用setClass再定义,防止误操作
下面的参数在R 3.0.0版以后都被弃用了,虽仍可用但应尽量避免:
- S3methods
- representation
- access
- version
2.2 通过读取数据产生S4类对象
这是考虑用户层次和使用体验的内容,对R软件开发者来说也相当简单:编写几个函数,通过读取不同类型的文件返回S4类对象。一般软件包都会替用户考虑这点很起码的要求。比如Affy包,用ReadAffy函数就获得了AffyBatch类对象,不用关心AffyBatch类是个什么东西。很多人可能根本就不想知道S3或S4是什么玩意,伤脑筋。
3 虚拟类
4 类联合
5 S4泛型函数和方法
5.1 定义的一般过程
S4的泛型函数通过调用setGeneric函数产生,该函数的用法如下:
## NOT RUNsetGeneric(name, def = , group = list(), valueClass = character(), where = , package = , signature = , useAsDefault = , genericFunction = , simpleInheritanceOnly = )
参数虽然很多,但常用两个:
- name: 表示泛型函数名称的字符串。这个函数必需已经定义,它将被转成泛型函数(如果它还不是泛型函数),而且该函数将被设为默认方法
- def: 这是可选项。如果name参数没有对应的已有函数,这一项必需提供。如果name已经有对应的函数,使用def项可以指定其他的函数作为泛型函数(偷梁换柱)。
泛型函数的方法使用setMethod函数进行定义:
## NOT RUNsetMethod(f, signature = character(), definition, where = topenv(parent.frame()), valueClass = NULL, sealed = FALSE)
- f:泛型函数名称
- signature:识别指纹,即对象的类名称
在R终端输入变量名后回车会得到变量的内容,其实质是调用show函数显示变量。下面通过设置泛型函数及其方法重定义show函数输出Anybody类对象的内容:
## 重定义前show函数输出的内容xx
## An object of class "Anybody"## Slot "skin":## [1] "Y"## ## Slot "name":## [1] "Adam"## ## Slot "gender":## [1] M## Levels: M## ## Slot "food":## [1] "rice"## ## Slot "drink":## [1] "water"
setGeneric(name = "show")
## [1] "show"
setMethod("show", signature = "Anybody", function(object) show(object@drink))
## [1] "show"
## 重定义后show函数输出的内容xx
## [1] "water"
5.2 设置获取类对象内容的辅助函数
5.3 设置S4类对象赋值(内容替换)方法
5.4 类转换
用于S4类转换的函数是as。如果定义新类时使用了contains获得了继承关系,那么就可以用as函数将一个对象强制转换为其父类对象,对象的内容是父类定应的相应部分内容:
class(xx)
## [1] "Anybody"## attr(,"package")## [1] ".GlobalEnv"
as(xx, "Diet")
## An object of class "Diet"## Slot "food":## [1] "rice"## ## Slot "drink":## [1] "water"
as(xx, "Somebody")
## An object of class "Somebody"## Slot "name":## [1] "Adam"## ## Slot "gender":## [1] M## Levels: M
Author: ZGUANG@LZU
Created: 2014-03-04 二 13:42
Emacs 24.3.1 (Org mode 8.2.1)
Validate
- R语言OOP(3):S4的实现方法
- R语言OOP(2):S3 OOP 的实现方法
- R语言的S4类
- R语言S3、S4方法的定义以及实例化
- R语言-S4系统
- R语言面向对象S4
- R语言:特殊数据类型S4
- 深入了解R语言-S4
- R语言里S4对象的泛型函数
- R语言基于S4的面向对象编程
- R语言S4类应用的一个简单例子
- R语言OOP(1):基础
- C语言实现的OOP
- R语言之OOP篇
- R语言泛型函数及S3,S4对象机制
- 不同方法的正态性检验及R语言实现
- R语言中实现笛卡尔积的一个方法
- R语言的三种聚类方法
- R语言OOP(1):基础
- Spring REST是什么?
- C++11中的function和bind
- R语言OOP(2):S3 OOP 的实现方法
- 用R语言制作渐变背景图片
- R语言OOP(3):S4的实现方法
- java设计模式-结构化模式:适配器模式
- Debian系统安装与设置备忘
- 使用BioC软件包分析qPCR数据
- AVPlayer not playing video that needs cookie authorization.
- Emacs/ESS注释编辑技巧
- poj 1691 Painting A Board
- javaScript 面向对象-继承(四)
- 对数组名取地址是什么?