R 中的引用类与我们在 C++、Java、Python 等常用语言中看到的面向对象编程相似。
与 S3 和 S4 类不同,方法属于类而不是泛函。引用类在内部实现为 S4 类,并添加了一个环境。
如何定义引用类?
定义引用类类似于定义 S4 类。我们使用 setRefClass()
函数而不是 setClass()
。
> setRefClass("student")
如果定义了成员变量,则需要将其包含在类定义中。引用类的成员变量称为字段(类似于 S4 类中的插槽)。
以下是定义一个名为 student
的类,其中包含 3 个字段:name
、age
和 GPA
的示例。
> setRefClass("student", fields = list(name = "character", age = "numeric", GPA = "numeric"))
如何创建引用对象?
setRefClass()
函数返回一个生成器函数,用于创建该类的对象。
> student <- setRefClass("student",
fields = list(name = "character", age = "numeric", GPA = "numeric"))
> # now student() is our generator function which can be used to create new objects
> s <- student(name = "John", age = 21, GPA = 3.5)
> s
Reference class object of class "student"
Field "name":
[1] "John"
Field "age":
[1] 21
Field "GPA":
[1] 3.5
如何访问和修改字段?
可以使用 $
运算符访问对象的字段。
> s$name
[1] "John"
> s$age
[1] 21
> s$GPA
[1] 3.5
同样,可以通过重新赋值来修改它。
> s$name <- "Paul"
> s
Reference class object of class "student"
Field "name":
[1] "Paul"
Field "age":
[1] 21
Field "GPA":
[1] 3.5
警告提示
在 R 编程中,对象在赋值给新变量或传递给函数时会被复制(按值传递)。例如。
> # create list a and assign to b
> a <- list("x" = 1, "y" = 2)
> b <- a
> # modify b
> b$y = 3
> # a remains unaffected
> a
$x
[1] 1
$y
[1] 2
> # only b is modified
> b
$x
[1] 1
$y
[1] 3
但引用对象的情况并非如此。只有一个副本存在,所有变量都引用同一个副本。因此得名为引用。
> # create reference object a and assign to b
> a <- student(name = "John", age = 21, GPA = 3.5)
> b <- a
> # modify b
> b$name <- "Paul"
> # a and b both are modified
> a
Reference class object of class "student"
Field "name":
[1] "Paul"
Field "age":
[1] 21
Field "GPA":
[1] 3.5
> b
Reference class object of class "student"
Field "name":
[1] "Paul"
Field "age":
[1] 21
Field "GPA":
[1] 3.5
这可能会导致值发生一些不必要的更改,并成为奇怪错误的根源。我们在处理引用对象时需要牢记这一点。要创建副本,我们可以使用提供的 copy()
方法。
> # create reference object a and assign a’s copy to b
> a <- student(name = "John", age = 21, GPA = 3.5)
> b <- a$copy()
> # modify b
> b$name <- "Paul"
> # a remains unaffected
> a
Reference class object of class "student"
Field "name":
[1] "John"
Field "age":
[1] 21
Field "GPA":
[1] 3.5
> # only b is modified
> b
Reference class object of class "student"
Field "name":
[1] "Paul"
Field "age":
[1] 21
Field "GPA":
[1] 3.5
引用方法
方法是为引用类定义的,不属于 S3 和 S4 类中的泛函。
所有引用类都预定义了一些方法,因为它们都继承自超类 envRefClass
。
> student
Generator for class "student":
Class fields:
Name: name age GPA
Class: character numeric numeric
Class Methods:
"callSuper", "copy", "export", "field", "getClass", "getRefClass",
"import", "initFields", "show", "trace", "untrace", "usingMethods"
Reference Superclasses:
"envRefClass"
我们可以在上面的列表中看到像 copy()
、field()
和 show()
这样的类方法。我们可以为类创建自己的方法。
这可以在类定义期间完成,方法是将函数定义列表传递给 setRefClass()
的 methods
参数。
student <- setRefClass("student",
fields = list(name = "character", age = "numeric", GPA = "numeric"),
methods = list(
inc_age = function(x) {
age <<- age + x
},
dec_age = function(x) {
age <<- age - x
}
)
)
在我们代码的以上部分,我们定义了两个名为 inc_age()
和 dec_age()
的方法。这两个方法修改了 age
字段。
请注意,由于 age 不在方法的局部环境中,我们必须使用非局部赋值运算符 <<-
。这一点很重要。
使用简单的赋值运算符 <-
会创建一个名为 age
的局部变量,这并非我们想要的。在这种情况下,R 会发出警告。
这是一个我们使用上述定义方法的示例运行。
> s <- student(name = "John", age = 21, GPA = 3.5)
> s$inc_age(5)
> s$age
[1] 26
> s$dec_age(10)
> s$age
[1] 16