R和面向对象编程
面向对象的编程在R中是非常有可能的。然而,与Python不同的是,实现面向对象的方法有很多种:
- R.oo包
- S3和S4class
- 参考类
- 原始包裹
我的问题是:
R中面向对象程序devise的主要区别是什么?
理想情况下,这里的答案将作为R程序员试图确定哪种OO编程方法最适合他们的需求的参考。
因此,我所要求的是细节,以客观的方式,以经验为依据,并以事实和依据为依据。 附加点澄清这些方法如何映射到标准的面向对象的做法。
S3class
- 不是真正的对象,更多的是命名约定
- 基于。 语法:例如打印,
print
调用print.lm
print.anova
等。如果没有find,print.default
S4class
- 可以派发多个参数
- 比S3更复杂的实现
参考类
- 主要用于避免复制大型对象(通过引用传递)
- 描述使用RefClasses的原因
原
- ggplot2最初是用proto编写的,但最终会用S3重写。
- 整洁的概念(原型,而不是类),但在实践中似乎很棘手
- ggplot2的下一个版本似乎正在远离它
- 描述的概念和实施
R6类
- 通过参考
- 不依赖S4class
- “ 创build一个R6类与引用类相似,除了不需要分隔字段和方法,而且不能指定字段的types”。
2012年3月8日编辑:下面的答案回应了原来发布的问题,这个问题已经被删除。 我已经复制下面,为我的答案提供上下文:
不同的OO方法如何映射到比如Java或Python中更标准的OO方法?
我的贡献涉及到你的第二个问题,关于R的OO方法如何映射到更多的标准OO方法。 正如我以前想到的那样,我一次又一次地回到了两个段落,一个是Friedrich Leisch,另一个是John Chambers。 他们都做了很好的阐述,为什么类似于O的编程在R中有着与其他语言不同的风格。
首先,来自“创buildR包:教程”的Friedrich Leisch( 警告:PDF ):
S是罕见的,因为它是互动的,并有一个面向对象的系统。 清楚地devise类是编程,但是为了使S成为一个交互式数据分析环境很有用,它是一种function语言是合理的。 在“真正的”面向对象编程(OOP)语言(如C ++或Java)中,类和方法定义紧密地绑定在一起,方法是类(以及对象)的一部分。 我们希望像用户定义的方法那样增加和交互式添加预定义的类。 这些添加可以在任何时间点进行,即使在我们分析数据集的时候也可以在命令行提示符下进行。 S试图在面向对象和交互式使用之间做出妥协,虽然妥协对于他们试图达到的所有目标来说都不是最佳的,但它们在实践中经常出乎意料地工作。
另一段来自约翰·钱伯斯(John Chambers)出色的书籍“数据分析软件”(Data for Data Analysis) 。 ( 链接到引用的段落 ):
尽pipeS和其他一些函数式语言支持类和方法,但OOP编程模型与S语言完全不同。 OOP系统中的方法定义是本地的; 没有要求对于一个无关的类来说,一个方法的同一个名字意味着同样的事情。 相比之下,R中的方法定义不在类定义中; 从概念上讲,它们与通用function相关联。 类定义直接或通过inheritance进入确定方法select。 程序员习惯于OOP模型有时会感到沮丧或困惑,他们的编程不会直接传递给R,但它不能。 方法的function性使用比较复杂,但也更加适合具有有意义的function,不能简化为OOP版本。
S3和S4似乎是面向对象编程的官方(即内置)方法。 我已经开始使用S3与embedded在构造函数/方法中的函数的组合。 我的目标是有一个对象$ method()types的语法,所以我有半私人领域。 我说半私人的,因为没有办法真正隐藏他们(据我所知)。 下面是一个简单的例子,它并没有做任何事情:
#' Constructor EmailClass <- function(name, email) { nc = list( name = name, email = email, get = function(x) nc[[x]], set = function(x, value) nc[[x]] <<- value, props = list(), history = list(), getHistory = function() return(nc$history), getNumMessagesSent = function() return(length(nc$history)) ) #Add a few more methods nc$sendMail = function(to) { cat(paste("Sending mail to", to, 'from', nc$email)) h <- nc$history h[[(length(h)+1)]] <- list(to=to, timestamp=Sys.time()) assign('history', h, envir=nc) } nc$addProp = function(name, value) { p <- nc$props p[[name]] <- value assign('props', p, envir=nc) } nc <- list2env(nc) class(nc) <- "EmailClass" return(nc) } #' Define S3 generic method for the print function. print.EmailClass <- function(x) { if(class(x) != "EmailClass") stop(); cat(paste(x$get("name"), "'s email address is ", x$get("email"), sep='')) }
还有一些testing代码:
test <- EmailClass(name="Jason", "jason@bryer.org") test$addProp('hello', 'world') test$props test class(test) str(test) test$get("name") test$get("email") test$set("name", "Heather") test$get("name") test test$sendMail("jbryer@excelsior.edu") test$getHistory() test$sendMail("test@domain.edu") test$getNumMessagesSent() test2 <- EmailClass("Nobody", "dontemailme@nowhere.com") test2 test2$props test2$getHistory() test2$sendMail('nobody@exclesior.edu')
这里是一个博客文章的链接,我写了关于这种方法: http : //bryer.org/2012/object-oriented-programming-in-r我欢迎评论,批评,并build议这种方法,因为我不相信我自己如果这是最好的办法。 但是,对于我试图解决的问题已经很好了。 具体来说,对于makeR包( http://jbryer.github.com/makeR ),我不希望用户直接更改数据字段,因为我需要确保代表我的对象状态的XML文件将保持同步。 只要用户遵守我在文档中列出的规则,就可以很好地工作。