R 学习笔记:R 面向对象编程

R 语言虽然看上去是面向过程的语言, 其实 R 语言也是有很好的面向对象编程的特性的. R 中有三个面向对象的体系: S3; S4; Reference classes. S3 和 S4 是很典型的 R 对象, 其修改实际上是对原对象的复制, 并且其对象的方法并不属于对象本身. 而 Reference classes, 或者 RC, 则是十分像是底层的 C++ 的对象, 其对对象的修改是直接的修改, 而不是复制, 并且, 其方法是对象的一部分, RC 更像是 S4 和其环境和方法的整合. 这里主要关注 S3 和 S4 这两个系统.


S3

S3 是 R 的第一个非常简单的面向对象的体系, 也是使用最广泛的体系. 一般初学 R, 使用的就主要是 S3. 在 basic 包和 stats 包中所采用的也都是 S3. 可以说 S3 是 R 面向对象编程或者平时的数据处理的基础吧.

判断一个对象是不是 S3 对象, 可以使用函数 is.object 和 isS4, S3 对象是对象, 所以 is.object 会返回 TRUE, 而 isS4 则返回 FALSE. 也可以使用 pryr 包中的 otype 函数去检验, otype 会返回四种结果: base; S3; S4; RC. 如果一个对象没有 class 这个属性, 则返回 "base"; 如果对象有 class 属性, 而不是 S4, 则返回 "S3"; 如果一个对象是 S4, 而不是 RC, 则返回 "S4"; 同样, 如果一个对象是 Reference classes, 则返回 "S4".

因为 S3 的类实际上是对象的一个属性, 所以可以通过一般的改变属性的办法来建立对象. 可以使用 structure 或者 attr 函数来设置 class 属性. 对于已经建立的对象, 还可以使用 class 替换函数来修改属性, 可以使用 class 函数来设置 class 的属性, 也可以使用 unclass 来去除 class 的属性. 还可以使用 inherits 函数来看继承关系.

foo <- structure(list(), class = "foo")
class(foo)
#[1] "foo"
inherits(foo, "foo")
#[1] TRUE

建立新方法

对于新建立的类, 可以使用 UseMethod 函数来建立适合该类的方法. UseMethod 接受函数名做为参数. 然后接着建立对于不同的类的方法, 应该使用函数名加上一个小数点再加上类名作为类的方法函数.

thefoo <- function(x) UseMethod("thefoo")
thefoo.default <- function(x) "Default"
thefoo.foo <- function(x) "Class foo"
foo <- structure(list(), class = "foo")
thefoo(foo)
#[1] "Class foo"

S4

S4 和 S3 很像, 同样方法不属于类, 而属于函数. 但是 S4 相比于 S3 有更为严格的定义. 还需要注意的是, S3 使用 "$" 来取元素, 而 S4 则使用 "@" 来取元素.

S4 的方法都在 methods 包中, 由于该包不是在所有情况下都加载, 所以最好使用 S4 前调用该包. 由于 basic 包和 stats 包是建立在 S3 上的, 所以需要调用 stats4 包来使用相关的函数.

S4 的建立不像 S3 那么简单只要设置属性就了. S4 的建立需要有相对严格的定义, 通过 setClass 函数来设置一个类的名称, 存储槽 (slots), 还有继承关系 (contains), 还有更多可以设置的东西, 可以查看文档了解. 而建立一个 S4 对象, 需要使用 new 函数, 需要指明类名, 每个存储槽的值.

setClass("Person",
         slots = list(name = "character", age = "numeric"))
alice <- new("Person", name = "Alice", age = 40)
setClass("Employee",
         slots = list(boss = "Person"),
         contains = "Person")
john <- new("Employee", name = "John", age = 20, boss = alice)

选择对象的一个元素的时候, 需要使用 slot 函数或者 @, 相当于 S3 中的 "[[" 函数.

slot(john, "boss")
#An object of class "Person"
#Slot "name":
#[1] "Alice"

#Slot "age":
#[1] 40

建立新方法

S4 为类建立新方法会相对于 S3 麻烦一点. 建立新方法时需要使用 setGeneric 和 setMethod 函数.

setGeneric("union")
#[1] "union"
setMethod("union",
          c(x = "data.frame", y = "data.frame"),
          function(x, y) {
          unique(rbind(x, y))
          }
)
#[1] "union"

RC

RC 本身继承自 S4, RC 的建立方法和 S4 很像, 使用 setRefClasses 函数. RC 和 S4 的重要区别在于方法属于类本身, 所以创建新的对象使用的是类的 new 函数.

Account <- setRefClass("Account",
                       fields = list(balance = "numeric"),
                       methods = list(
                       withdraw = function(x) {
                           balance <<- balance - x
                       },
                       deposit = function(x) {
                           balance <<- balance + x
                       }))
a <- Account$new(balance = 100)
a$balance
#> [1] 100
a$balance <- 200
a$balance
#> [1] 200

RC 的复制不是简单的使用赋值符号, 而应该使用 copy 方法.

b <- a$copy()
By @Wolfson Liu in
Tags : #r,