专注系列化、高质量的R语言教程
本系列将介绍R语言中三个与面向对象的编程(Object-Oriented Programming,OOP)相关的工具包:proto、R6和基础包methods。这是一个承上启下的系列,上承《自定义ggplot2绘图系统函数》系列,下启《基于mlr3工具包的机器学习》系列(该系列将在本系列之后推出)。这两个系列的编程风格都属于OOP,前者基于proto包的proto对象,后者基于R6包的R6类。
本篇推文介绍的是proto对象,在推文定义一个简单的统计变换函数中已有了初步介绍。本篇将介绍如下内容:
创建proto对象
proto对象和普通对象的区别
proto对象备份
proto对象委托
1 创建proto对象
OOP风格又分为两种风格:一种是基于对象的编程(Object-Based Programming),也称原型编程(Prototype Programming);另一种是基于类的编程(Class-Based Programming)。proto工具包采用的是前者。
因为没有“类”的概念,原型编程可以更自由地定义对象。从形式上看,定义proto对象与定义列表(list)很相似,使用的是proto()函数:
library(proto)
oo <- proto(
x = 1,
add = function(.) {.$x = .$x + 1}
)
如上代码就定义了一个proto对象oo。proto对象的元素分为两类:一类称为属性(field),在R中就是变量(variable),如oo中的x;另一类称为方法(method),在R中就是函数(function),如oo中add。
proto对象中的元素也可以使用符号$进行引用:
oo$x
## [1] 1
oo$add()
因为
add()函数没有返回值,因此语句oo$add()输出内容为空。
在proto对象内部的函数中,使用点号.表示对象本身。因此.$x = .$x + 1意味着add()函数每运行一次,就会使元素x在原有的基础上加1:
oo$x
## [1] 2
oo$add()
oo$x
## [1] 3
这种对象是显著不同于数据框、列表等普通对象的。对于面向过程的编程而言,没有赋值符号的代码只起到输出对象内容的作用,而不会改变自身或其他对象的内容。
当然,proto对象中的函数不一定要改变其元素的值,也可以输出结果(函数有输出结果的方法见推文如何自定义R语言函数):
## 定义
oo2 <- proto(
x = 1:5,
add = function(.) sum(.$x)
)
## 运行
sum(oo2$x)
## [1] 15
oo2$add()
## [1] 15
2 与普通对象的区别
对于普通对象来说,有如下代码:
a <- 2
b <- a
b
## [1] 2
通过上述代码,我们将对象a的当前值传递给对象b。但是,a和b是完全两个独立的对象,a后来的变化不会影响到b:
a <- 3
b
## [1] 2
通过上述代码,我们修改了a的值,而b的值不会随之变化。
对于proto对象则不同。我们先将oo2对象“复制”给oo3,此时oo2$x和oo3$x显然是相同的:
oo3 <- oo2
oo2$x
## [1] 1 2 3 4 5
oo3$x
## [1] 1 2 3 4 5
之后,我们再修改oo2$x的值,可以发现oo3$x也会随之改变:
oo2$x <- oo2$x + 1
oo2$x
## [1] 2 3 4 5 6
oo3$x
## [1] 2 3 4 5 6
对于proto对象而言,赋值符号<-起到的不是“备份”的作用,而是“重命名”的作用。在本例中,也就是说oo2和oo3只是同一对象的两个名称而已。通过下面代码可以看出,二者指向的是同一对象,因此改变其中一个,另一个也会随之变化:
oo2
## <environment: 0x000002133f8b4ae8>
## attr(,"class")
## [1] "proto" "environment"
oo3
## <environment: 0x000002133f8b4ae8>
## attr(,"class")
## [1] "proto" "environment"
3 proto对象备份
因为proto对象可以进行自我更新,因此有必要提前对其进行备份。但是上节的方法起不到“备份”效果,那该如何进行备份呢?方法是比较固定的,例如:
1056

被折叠的 条评论
为什么被折叠?



