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文件将保持同步。 只要用户遵守我在文档中列出的规则,就可以很好地工作。