类系统
查核在R中的三级系统, S3,S4和参考ce类。
## S3 methods, Section 5 of
RShowDoc("R-lang")
## S4 classes
?Classes
?Methods
## Reference classes
?ReferenceClasses
随着你会被诱惑去参考类的Java背景,但是这些“引用语义”和超距作用(改变一个对象改变另一种是指相同的数据),而最R用户期望'copy on change'语义。一个人可以在S3班上取得很大的进步,但是我认为采取更严谨的方法会采用S4。 S4的特点会让你大吃一惊,部分原因是类系统更接近于普通的lisp对象系统而不是java。
还有other opinions and options。
基本实现
我真的不知道你的设计目标与'过程数据”是什么;我将把你的两个类实现为一个类,一个泛型和一个在MyClass类上运行的泛型的方法。
## definition and 'low-level' constructor
.MyClass <- setClass("MyClass", representation(word="character"))
## definition of a generic
setGeneric("processData", function(x, ...) standardGeneric("processData"))
setMethod("processData", "MyClass", function(x, ...) {
cat("processData(MyClass) =", [email protected], "\n")
})
这是完整的,功能齐全的
> myClass <- .MyClass(word="hello world")
> processData(myClass)
processData(MyClass) = hello world
三个代码行可能被放置在两个文件中,“AllGenerics.R”和“MyClass.R”(包括法)或三个文件“AllGenerics.R”,“AllClasses.R”,“processData-methods.R”(注意方法与泛型相关联,并在类上派发)。
其他实施
人们通常会增加一个更人性化的构造函数,例如提供提示给用户关于预期的数据类型或进行复杂的参数初始化步骤
MyClass <- function(word=character(), ...)
{
.MyClass(word=word, ...)
}
通常一个想要一个插槽accesssor,而不是直接插槽访问。这可以是一个简单的函数(如图所示)或一个通用的+方法。
word <- function(x, ...) [email protected]
如果要更新插槽,则会写入替换函数或方法。该函数或方法通常有三个参数,即要更新的对象,可能的附加参数以及用于更新对象的值。这里有一个通用的方法+实施
setGeneric("word<-", function(x, ..., value) standardGeneric("word<-"))
setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) {
## note double dispatch on x=MyClass, value=character
[email protected] <- value
x
})
一个有点棘手替代实施
setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) {
initialize(x, word=value)
})
它使用initialize
通用的,默认的方法作为拷贝构造函数;如果同时更新多个插槽,这可能很有效。
由于该类用户看到,一个人想用“秀”的方法,为此,通用()已经存在
setMethod("show", "MyClass", function(object) {
cat("class:", class(object), "\n")
cat("word:", word(object), "\n")
})
现在我们的用户在用户友好的方式来显示它会话看起来像
> myClass
class: MyClass
word: hello world
> word(myClass)
[1] "hello world"
> word(myClass) <- "goodbye world"
> processData(myClass)
processData(MyClass) = goodbye world
效率
ř有效地工作于载体; S4班也不例外。所以设计是一个类的每个插槽表示跨越多行的列,而不是单行的元素。我们期望插槽'word'通常包含一个长度大于1的向量,并且操作可以作用于向量的所有元素。所以一个会写一点,比如这个方法,修改show方法来
setMethod("show", "MyClass", function(object) {
cat("class:", class(object), "\n")
cat("word() length:", length(word(object)), "\n")
})
这里有更大的数据对象(使用文件我的Linux系统上)
> amer <- MyClass(readLines("/usr/share/dict/american-english"))
> brit <- MyClass(readLines("/usr/share/dict/british-english"))
> amer
class: MyClass
word() length: 99171
> brit
class: MyClass
word() length: 99156
> sum(word(amer) %in% word(brit))
[1] 97423
> amer_uc <- amer ## no copy, but marked to be copied if either changed
> word(amer_uc) <- toupper(word(amer_uc)) ## two distinct objects
而这一切是相当高性能。引用类的
危险“行动在超距”
让我们倒带,以更简单的实现了S4类的,直接槽接和没有花哨的构造。这里是美国词典和复制,转换为大写
.MyClass <- setClass("MyClass", representation(word="character"))
amer <- .MyClass(word=readLines("/usr/share/dict/american-english"))
amer_uc <- amer
[email protected] <- toupper([email protected])
注意我们大写的amer_uc
但不amer
:
> [email protected][99 + 1:10]
[1] "Adana" "Adar" "Adar's" "Addams" "Adderley"
[6] "Adderley's" "Addie" "Addie's" "Addison" "Adela"
> [email protected][99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
这真的是 - [R用户期待 - 我创建了一个单独的对象并对其进行了修改;原始对象是未修改的。这是我的一个断言;也许我不知道R用户的期望。我假设R用户并没有真正关注这是一个参考类,但认为它只是另一个R对象,如integer()
矢量或data.frame
或返回值lm()
。
相反,这里有一个最小的实现引用类的,和类似的操作
.MyRefClass <- setRefClass("MyRefClass", fields = list(word="character"))
amer <- .MyRefClass(word=readLines("/usr/share/dict/american-english"))
amer_uc <- amer
amer_uc$word <- toupper(amer_uc$word)
但现在我们已经改变了这两个amer
和amer_uc
!完全由C或Java程序员预期,但不是R用户。
> amer$word[99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
> amer_uc$word[99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
这是可能的吗?参考?如果这是个问题,当然可以将你的程序的不同部分放在不同的文件中。如果这是个问题,参考类就像伪代码一样工作。请参阅'?setRefClass'。 – 2013-04-20 13:24:13
请注意,面向对象的编程风格并非R最自然的东西,它具有更多的编程风格。 – 2013-04-20 15:23:38
面向对象程序设计对R来说是非常不自然的,它已经实现了大约四五次,而功能风格的程序设计只用了一次S语言本身,并且从那以后几乎没有改变。 – Spacedman 2013-04-20 17:07:12