R语言面向对象编程:S3和R6
一、基于S3的面向对象编程
基于S3的面向对象编程是一种基于泛型函数(generic function)的实现方式。
1.S3函数的创建
S3对象组成:generic(generic FUN)+method(generic.class FUN)
泛型函数(generic)创建示例:
get_n_elements <- function(x,...)
{
UseMethod("get_n_elements")
}
通常用UseMethod()函数定义一个泛型函数的名称,通过传入参数的class属性,来确定对应的方法调用。
method(generic.class)函数,创建示例:
# Create a data.frame method for get_n_elements
get_n_elements.data.frame <- function(x, ...)
{
nrow(x) * ncol(x) # or prod(dim(x))
} # Create a default method for get_n_elements
#在使用UseMethod调用时,先在methods中寻找对应class,如果都没有找到,则会调用#default方法。
get_n_elements.default <- function(x,...)
{
length(unlist(x))
}
methods() 用于查找S3泛型函数中所有可用的methods。
调用pryr包中的is_s3_method() 可以验证函数是否S3方法。
2、S3对象的传入参数有多个class属性的处理方法
当变量x具有多个class属性,应按具体到通用的顺序来排列变量对应的class。
使用NextMethod()来调用methods
an_s3_method.some_class <- function(x, ...)
{
# Act on some_class, then
NextMethod("an_s3_method")
}
具体示例如下:
# Assign classes
class(kitty) <- c("cat","mammal","character") what_am_i <- function(x,...)
{
UseMethod("what_am_i")
} # cat method
what_am_i.cat <- function(x)
{
message("I'm a cat")
NextMethod("what_am_i")
} # mammal method
what_am_i.mammal <- function(x, ...)
{
message("I'm a mammal")
NextMethod("what_am_i")
} # character method
what_am_i.character <- function(x, ...)
{
message("I'm a character vector")
}
二、基于R6的面向对象编程
1、R6对象的创建
首先使用 R6Class() 创建一个class generator(也可叫factory)。
第一个参数是创建的对象的类的名字。
参数private为一个list,为对象保存数据域(data field),包含每个元素的名字。
参数pubilc为一个list,为对象保存面向用户的函数或功能。
library(R6)
thing_factory <- R6Class(
"Thing",
private = list(
a_field = "a value",
another_field = 123
),
public = list(
do_something = function(x, y, z) {
# Do something here
}
)
)
创建factory后,再调用new()来生成一个R6对象。new()无需定义,所有的factory都默认具有该方法。
a_thing <- thing_factory$new()
initialize()是一种特殊的公有方法(public method), 在R6对象创建时自动调用,用来设置私域(private field)值。
new()中的参数被传给initialize()。
# Add an initialize method
microwave_oven_factory <- R6Class(
"MicrowaveOven",
private = list(
power_rating_watts = 800,
door_is_open = FALSE
),
public = list(
cook = function(time_seconds) {
Sys.sleep(time_seconds)
print("Your food is cooked!")
},
open_door = function() {
private$door_is_open = TRUE
},
close_door = function() {
private$door_is_open = FALSE
},
# Add initialize() method here
initialize = function(power_rating_watts,door_is_open){
if(!missing(power_rating_watts)){
private$power_rating_watts<-power_rating_watts
}
if(!missing(door_is_open)){
private$door_is_open<-door_is_open
}
}
)
) # Make a microwave
a_microwave_oven <- microwave_oven_factory$new(
power_rating_watts = 650,
door_is_open = TRUE
)
2.访问和设置私有域
private组件中的数据对用户隐藏,这是封装的原理。使用private$前缀可以访问私域(private field)。
存在active组件中的active binding(行为像变量的函数),可以获取和设置私有数据域。
Active bindings是R6中一种特殊的函数调用方式,把对函数的访问表现为对属性的访问,属于公有组件。
thing_factory <- R6Class(
"Thing",
private = list(
..a_field = "a value"
),
active = list(
a_field = function(value) {
if(missing(value)) {
private$..a_field
} else {
assert_is_a_string(value) # or another assertion
private$..a_field <- value
}
}
)
)
将active binding作为数据变量而不是函数进行调用。
a_thing <- thing_factory$new()
a_thing$a_field # not a_thing$a_field()
3、R6的继承
(1)子类的创建
继承将一个类(class)的功能复制到另一个。可在R6Class()中用inherit参数来创建子类。
child_class_factory <- R6Class(
"ChildClass",
inherit = parent_class_factory
)
子类可以添加公有方法来扩展父类的功能,并在公有方法中使用前缀self$调用其他公有方法。
子类也可定义与父类相同的方法名称来重写该方法,用于扩展父类的功能,子类使用前缀super$访问父类的公有方法。
(2)跨级访问
R6类默认只能使用直接父类的功能。为了跨级访问,中间类(intermediate classes)首先需要定义一个active binding来显示父类,形式如下:
active = list(super_ = function() super)
然后可以跨级使用方法parent_method <- super$method()grand_parent_method <- super$super_$method(great_grand_parent_method <- super$super_$super_$method()
(3)共享域 大部分类型的R变量是通过值复制,意思是当复制一个变量时 ,新的变量具有自己的复制值,改变其中一个变量不会影响另外一个。
环境变量(Environments)比较特殊,通过地址传送来复制(by reference),因此所有的复制品都是等同的,改变其中一个就会改变其他变量。R6类可利用环境的地址传送(by reference)复制行为在对象之间共享区域,定义一个名为shared的私域,方式如下:
创建一个新的环境,指定该环境的任意共享域,可通过active bindings访问。
工作方式与其他active bindings相同,但需使用private$shared$前缀来找回(retrieve)这些区域
R6Class(
"Thing",
private = list(
shared = {
e <- new.env()
e$a_shared_field <- 123
e
}
),
active = list(
a_shared_field = function(value) {
if(missing(value)) {
private$shared$a_shared_field
} else {
private$shared$a_shared_field <- value
}
}
)
)
(4)R6对象的复制
R6对象使用与环境变量相同的地址传送复制方式,如果使用<- 符号复制R6对象,原对象的变化会影响复制品。
a_reference_copy <- an_r6_object
R6 对象有一个自动生成的clone() 方法,用于值复制,使用clone()复制的对象的变化不影响其他对象。
a_value <- an_r6_object$clone()
当R6对象的一个或多个域包含另一个R6对象时,默认clone() 通过地址传送复制该R6域。
如果值复制这些R6域,clone() 方法必须使用参数:deep = TRUE。
a_deep_copy <- an_r6_object$clone(deep = TRUE)
(5)R6对象的消除
当消除对象时,定义公有finalize()方法运行自定义代码。该方法一般用于关闭与数据库或文件的连接,或者消除例如改变全局选项(option)或者图形参数的副作用。
thing_factory <- R6Class(
"Thing",
public = list(
initialize = function(x, y, z) {
# do something
},
finalize = function() {
# undo something
# Print a message
"Disconnecting from the cooking times database."
# Disconnect from the database
dbDisconnect(private$conn)
}
)
)
当R的自动垃圾回收器(automated garbage collector)从存储中清除对象时调用finalize() 方法,可使用gc()强制垃圾回收。
R语言面向对象编程:S3和R6的更多相关文章
- 云风:我所偏爱的C语言面向对象编程范式
面向对象编程不是银弹.大部分场合,我对面向对象的使用非常谨慎,能不用则不用.相关的讨论就不展开了. 但是,某些场合下,采用面向对象的确是比较好的方案.比如 UI 框架,又比如 3d 渲染引擎中的场景管 ...
- 零基础入门该如何实现C 语言面向对象编程(很有帮助)
零基础如果更快更好的入门C语言,如何在枯燥的学习中找到属于自己的兴趣,如果把学习当成一种事务性的那以后的学习将会很难有更深入的进步,如果带着乐趣来完成学习那将越学越有意思这样才会让你有想要更深入学习的 ...
- R语言高性能编程,优化(一)
这段时间学习了<R高性能编程>这本书,基于这段时间做的项目实践,总结了一些自己的体会,和大家分享 一.为什么R程序有时候会很慢?1.计算性能的三个限制条件 cpu ram io R代码本身 ...
- R语言高性能编程(三)
一.使用并行计算加倍提升性能1.数据并行 VS 任务并行实现数据并行的算法scoket 并行性注意并行计算时间并不与执行任务的计算资源数目成正比(计算机核心),amdahl定律:并行代码的速度受限于串 ...
- R语言高性能编程(二)
接着上一篇 一.减少内存使用的简单方法1.重用对象而不多占用内存 y <- x 是指新变量y指向包含X的那个内存块,只有当y被修改时才会复制到新的内存块,一般来说只要向量没有被其他对象引用,就可 ...
- C语言面向对象编程(五):单链表实现(转)
这里实现的单链表,可以存储任意数据类型,支持增.删.改.查找.插入等基本操作.(本文提供的是完整代码,可能有些长.) 下面是头文件: #ifndef SLIST_H #define SLIST_H # ...
- Rserve详解,R语言客户端RSclient【转】
R语言服务器程序 Rserve详解 http://blog.fens.me/r-rserve-server/ Rserve的R语言客户端RSclient https://blog.csdn.net/u ...
- R语言中文社区历史文章整理(类型篇)
R语言中文社区历史文章整理(类型篇) R包: R语言交互式绘制杭州市地图:leafletCN包简介 clickpaste包介绍 igraph包快速上手 jiebaR,从入门到喜欢 Catterpl ...
- R语言基于S4的面向对象编程
前言 本文接上一篇文章 R语言基于S3的面向对象编程,本文继续介绍R语言基于S4的面向对象编程. S4对象系统具有明显的结构化特征,更适合面向对象的程序设计.Bioconductor社区,以S4对象系 ...
随机推荐
- python服务器环境搭建(3)——参数配置
前面我们已安装好了python服务器运行所需要的相关软件,而最重要最繁琐的就是参数配置,写这篇就踩了好多坑,花了好多时间,遇到了各种各样的问题.好了费话少说,直接进入本篇话题. PS:本人不是专业的运 ...
- 自适应的tab菜单栏
代码部分: Css代码:*{ margin:0px; padding:0px; font-size:62.5%;}body{ background-color:#FFFFFF;}.zw-test-ti ...
- GitHub 添加 SSH keys
首先在本地创建 SSH Keys $ ssh-keygen -t rsa -C "18817801185@163.com" 后面的邮箱即为 github 注册邮箱,之后会要求确认路 ...
- C#研究OpenXML之路(1-新建工作簿文件)
一.写在开头 一直想沉下心来研究研究OpenXML编程,可是由于公司编程项目一笔接一笔,很难静下来,所以一直是采用的COM操作Excel.现在终于得闲,特将心得历程记录下来. 今天的第一个实例代码是来 ...
- 性能测试培训:批量执行Jmeter脚本之ant调用
性能测试培训:批量执行Jmeter脚本之ant调用 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的load ...
- 对象的创建过程(chapter5.7.3)
总结一下对象的创建过程,假设有一个名为Dog的类: 1. 即使没有显示地使用static关键字,构造器实际上也是静态的方法,因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类 ...
- AndroidStudio升级后出现Refresh gradle project和connection timed out的原因和解决方法
笔者发现现在升级AndroidStudio不需要FQ了,于是在看到了升级提醒后手贱点击了升级.可悲剧的一幕发生了, 正在写的一个项目从上到下密密麻麻的错误,看了一下提示要求升级Gradle 那就升级吧 ...
- How To Use ggplot2
0. Preparation and Introduction ggplot2是R中新颖的数据可视化包,这得益于Leland Wilkinson在他的著作<The Grammar of Grap ...
- [Python] Spark平台下实现分布式AC自动机(一)
转载请注明出处:http://www.cnblogs.com/kirai/ 作者:Kirai 零.问题的提出 最近希望在分布式平台上实现一个AC自动机,但是如何在这样的分布式平台上表示这样的非线性数据 ...
- webpack搭建服务器,随时修改刷新
前提:1.对webpack有一定了解,本文不做介绍 2.安装node.js 手把手操作: 1.创建一个名为webpack-server的文件夹(随便取的) 2.cd到当前文件夹:cd webpack- ...