136

由于我对 R 相当陌生,因此我不知道 S3 方法和对象是什么。我发现有 S3 和 S4 对象系统,如果可能的话,有些人建议使用 S3 而不是 S4(请参阅http://google-styleguide.googlecode.com/svn/trunk/google-r-style 上的 Google 的 R 样式指南。 html )*。但是,我不知道 S3 方法/对象的确切定义。

更新:截至 2019 年,谷歌的 R 风格指南超链接现在在这里

4

6 回答 6

90

大多数相关信息可以通过查看?S3or找到?UseMethod,但简而言之:

S3 指的是一种方法分派的方案。如果您使用 R 一段时间,您会注意到有许多不同类型的对象的print,predictsummary方法。

在 S3 中,这通过以下方式工作:

  • 设置感兴趣对象的类(例如:方法调用的返回值glm有类glm
  • 提供一个带有通用名称(例如print)的方法,然后是一个点,然后是类名(例如 print.glm:)
  • 必须对这个通用名称print( )。

在旁观者的眼中,特别是对于您新创建的时髦模型配件包的用户来说,能够打字predict(myfit, type="class")predict.mykindoffit(myfit, type="class").

它还有很多内容,但这应该可以帮助您入门。这种基于对象的属性(类)调度方法的方式有很多缺点(C 纯粹主义者可能会因为害怕而彻夜难眠),但在很多情况下,它工作得很好。在当前版本的 R 中,已经实现了更新的方法(S4 和参考类),但大多数人仍然(仅)使用 S3。

于 2011-07-05T13:44:53.403 回答
55

为了让您开始使用 S3,请查看该median函数的代码。在命令提示符下键入median显示它的正文中有一行,即

UseMethod("median")

这意味着它是一种 S3 方法。换句话说,您可以median为不同的 S3 类提供不同的功能。要列出所有可能的中值方法,请键入

methods(median) #actually not that interesting.  

在这种情况下,只有一种方法,即默认方法,它可以为任何事情调用。您可以通过键入来查看该代码

median.default

一个更有趣的例子是print函数,它有许多不同的方法。

methods(print)  #very exciting

请注意,某些方法*的名称旁边有 s。这意味着它们隐藏在某些包的名称空间中。用于find找出它们所在的包。例如

find("acf")  #it's in the stats package
stats:::print.acf
于 2011-07-05T14:05:59.757 回答
39

来自http://adv-r.had.co.nz/OO-essentials.html

R 的三个 OO 系统在类和方法的定义方式上有所不同:

  • S3 实现了一种称为泛型函数 OO 的 OO 编程风格。这与大多数实现消息传递 OO 的编程语言(如 Java、C++ 和 C#)不同。通过消息传递,消息(方法)被发送到对象,对象决定调用哪个函数。通常,该对象在方法调用中具有特殊的外观,通常出现在方法/消息的名称之前:例如 canvas.drawRect("blue")。S3 不同。虽然计算仍然是通过方法执行的,但一种称为通用函数的特殊类型的函数决定调用哪个方法,例如 drawRect(canvas, "blue")。S3是一个非常随意的系统。它没有对类的正式定义。

  • S4 的工作方式与 S3 类似,但更正式。与 S3 有两个主要区别。S4 有正式的类定义,描述了每个类的表示和继承,并且有特殊的帮助函数来定义泛型和方法。S4 也有多重分派,这意味着泛型函数可以根据类的任意数量的参数来选择方法,而不仅仅是一个。

  • 引用类,简称为 RC,与 S3 和 S4 有很大的不同。RC 实现了消息传递 OO,所以方法属于类,而不是函数。$ 用于分隔对象和方法,因此方法调用看起来像 canvas$drawRect("blue")。RC 对象也是可变的:它们不使用 R 通常的 copy-on-modify 语义,而是在原地修改。这使他们更难推理,但允许他们解决用 S3 或 S4 难以解决的问题。

还有另一个系统不是完全面向对象的,但在这里提一下很重要:

  • 基本类型,作为其他 OO 系统基础的内部 C 级类型。基本类型主要使用 C 代码进行操作,但了解它们很重要,因为它们为其他 OO 系统提供了构建块。
于 2014-05-20T18:58:48.553 回答
12

我来到这个问题主要是想知道名字来自哪里。从这篇维基百科文章中可以看出,该名称指的是 R 所基于的 S 编程语言的版本。其他答案中描述的方法调度方案来自S,并根据版本进行了适当的标记。

于 2011-11-01T23:11:30.417 回答
10

尝试

methods(residuals)

其中列出了“residuals.lm”和“residuals.glm”等。这意味着当您拟合线性模型时,m 和类型residuals(m),将调用residuals.lm。当您拟合了广义线性模型时,将调用residuals.glm。这是一种颠倒的 C++ 对象模型。在 C++ 中,您定义了一个具有虚函数的基类,这些虚函数被派生类覆盖。在 R 中,您定义了一个虚拟(又名泛型)函数,然后您决定哪些类将覆盖该函数(又名定义一个方法)。请注意,执行此操作的类不需要从一个公共超类派生。我不同意通常更喜欢 S3 而不是 S4。S4 有更多形式主义(= 更多类型),这对于某些应用程序来说可能太多了。然而,S4 类可以像 C++ 中的类或结构一样定义。您可以指定某个类的对象由一个字符串和两个数字组成,例如:

setClass("myClass", representation(label = "character", x = "numeric", y = "numeric"))

使用该类的对象调用的方法可以依赖于具有这些成员的对象。这与 S3 类非常不同,后者只是一堆元素的列表。

使用 S3 和 S4,您调用成员函数 byfun(object, args)而不是 by object$fun(args)。如果您正在寻找类似后者的东西,请查看 proto 包。

于 2011-07-05T23:22:42.647 回答
3

这里是根据Hadley Wickham(RStudio 首席科学家)的“Advanced R, 2nd edition” (CRC Press, 2019)对众多R 对象系统的更新快速概述,这里有一个网络表示,基于关于对象的章节- 面向编程

高级 R 书籍封面

2015 年的第一版在这里有一个网络表示这里有关于 OO 的相应章节。

面向 OO 系统的方法

Hadley 定义了以下内容来区分 OO 编程的两种不同方法:

功能 OOP:方法(可调用的代码片段)属于泛型函数(不要与 Java/C#泛型方法混淆)。将这些方法视为位于全局查找表中。运行时系统根据函数的名称和传递给该函数的一个或多个参数的类型(或对象类)找到要执行的方法(这称为“方法分派”)。在语法方面,方法调用可能看起来像普通的函数调用:myfunc(object, arg1, arg2). 此调用将导致运行时查找与该对关联的方法(“myfunc”,typeof(object))或可能(“myfunc”,typeof(object),typeof(arg1),typeof(arg2))如果语言支持。在 R 的 S3 中,泛型函数的全名给出了(function-name, class)对。例如:mean.Date是计算日期平均值的方法。尝试methods("mean")用函数名列出泛型方法mean。例如,在 OO 先驱SmalltalkCommon Lisp Object SystemJulia中可以找到函数式 OOP 方法。Hadley 指出,“与 R 相比,Julia 的实现已经完全开发并且非常高效。”

封装的 OOP:方法属于对象或类,方法调用通常看起来像object.method(arg1, arg2). 这被称为封装,因为对象封装了数据(字段)和行为(方法)。将该方法视为位于附加到对象或对象的类描述的查找表中。运行时根据方法名称和可能的一个或多个参数的类型查找方法。这是在 C++、Java、C# 等“流行”OO 语言中发现的方法。

在这两种情况下,如果支持继承(可能是),运行时可能会向上遍历类层次结构,直到找到调用查找键的匹配项。

如何找出 R 对象属于哪个系统

library(sloop) # formerly, "pryr"
otype(mtcars)
#> [1] "S3"

R 对象系统

S3

  • 功能 OOP 方法。
  • 哈德利认为最重要的系统。
  • 最简单,最常见。R 使用的第一个 OO 系统。
  • 随附底座 R,用于整个底座 R。
  • 依赖于约定而不是强制保证。
  • 参见Chambers、John M 和 Trevor J Hastie。1992. “S 中的统计模型”。Wadsworth & Brooks/Cole 高级书籍和软件。
  • “高级 R,第 2 版”中 详细信息。

S4

  • 功能 OOP 方法。
  • 根据哈德利,第三个最重要的系统。
  • 重写 S3,因此类似于 S3,但更正式和更严格:它迫使您仔细考虑程序设计。适用于构建大型系统(例如Bioconductor项目)。
  • 在基础“方法”包中实现。
  • 参见:Chambers, John M. 1998。“数据编程:S 语言指南”。施普林格。
  • “高级 R,第 2 版”中 详细信息。

RC 又名“参考类”

  • 封装的 OOP 方法。
  • 带有底座 R。
  • 基于 S4。
  • RC 对象是特殊类型的 S4 对象,也是“可变的”。即不使用R 通常的copy-on-modify 语义,它们可以就地修改。请注意,可变状态很难推理并且是丑陋错误的来源,但可以在某些应用程序中导致更高效的代码。

R6

  • 封装的 OOP 方法。
  • 哈德利认为第二重要的系统。
  • 可以在R6包中找到(使用 安装library(R6)
  • 类似于 RC,但更轻且更快:它不依赖于 S4 或方法包。建立在 R 环境之上。也有:
    • 公共和私有方法
    • 活动绑定(字段,在访问时实际调用方法)
    • 跨包工作的类继承
    • 类方法(属于类并且可以通过self, private,访问实例的代码super)和成员函数(分配给字段的函数,但不是方法,只是函数)
  • 提供一种标准化的方式来逃避 R 的“修改时复制”语义
  • 请参阅包站点:“R6:R 封装的面向对象编程”
  • “高级 R,第 2 版”中 详细信息。

其他

还有其他的,比如R.oo(类似于 RC)、proto(基于原型,想想 JavaScript)和Mutatr。但是,“高级 R” 说:

除了广泛使用的 R6 之外,这些系统主要具有理论意义。它们确实有自己的优势,但很少有 R 用户知道和理解它们,因此其他人很难阅读并为您的代码做出贡献。

请务必阅读“Advanced R, 2nd edition”中关于权衡的章节。

于 2019-10-26T11:29:20.167 回答