美文网首页
R 面向对象编程(一)

R 面向对象编程(一)

作者: 名本无名 | 来源:发表于2021-03-20 10:52 被阅读0次

前言

1. 面向对象

面向对象编程(object-oriented programmingOOP)是一种编程范式,它将对象作为程序的基本单元,一个对象包含了数据以及操作数据的函数。

那什么是对象?对象是类(class)类的实例。

那什么又是类呢?

类是对现实事物的抽象,比如说,人类是对世界上所有人的总称,而你、我却是实实在在存在于现实中的,也就是一个个对象。

类的定义包含了对数据的描述以及对应的操作方法,比如,人应该有性别、年龄、身高、体重等固有特征,但是每个对象,也就是说虽然每个人的特征千差万别,但都有这些固定的属性客观存在的。

2. R 的面向对象编程

之前,我们对 R 的理解可能都是停留在函数式编程的概念里。也就是编写一个个函数,来处理不同的对象。

当然,目前 R 主要用于统计计算,而且代码量一般不会很大,几十或上百行。使用函数式的编程方式就可以很好的完成编程任务。

一般来说,在 R 中,函数式编程要比面向对象编程重要得多,因为你通常是将复杂的问题分解成简单的函数,而不是简单的对象。

那为什么我还要学习面向对象编程呢?

面向对象编程的优势是,能够使程序便于分析、设计、理解,提高重用性、灵活性和可扩展性。

R 中的 OOP 系统

  • base R 提供的:S3, S4reference classes (RC)

  • CRAN 包提供的:R6R.ooproto

S3

1.1 概念

S3 面向对象编程,是 R 中第一个也是最简单的 OOP 系统,广泛存在于早期开发的 R 包中,也是 CRAN 包最常用的系统。

S3 的实现是基于一种特殊的函数(泛型函数,根据传入对象的类型来决定调用哪个方法)

1.2 创建 S3 对象

注意:下面我们会使用 sloop 包提供的函数来帮助我们查看对象的类型

> install.packages('sloop')
> library(sloop)

首先,我们使用 attr 来创建一个 S3 对象

> a <- 1
> attr(a, 'class') <- 'bar'
> a
[1] 1
attr(,"class")
[1] "bar"

使用 classattr 获取对象的类型

> class(a)
[1] "bar"
> attr(a, 'class')
[1] "bar"

再用 sloop 包的 otype 来判断是何种对象

> otype(a)
[1] "S3"
> otype(1)
[1] "base"

我们也可以使用 structure 来构建一个 S3 对象

> b <- structure(2, class='foo')
> b
[1] 2
attr(,"class")
[1] "foo"
> otype(b)
[1] "S3"

还可以使用为 class(var) 赋值的方式构建

> x <- list(a=1)
> class(x)
[1] "list"
> otype(x)
[1] "base"

> class(x) <- 'foo'
> class(x)
[1] "foo"
> otype(x)
[1] "S3"

还可以将类属性设置为向量,为 S3 对象指定多个类型

> c <- structure(3, class=c('bar', 'foo'))
> class(c)
[1] "bar" "foo"
> otype(c)
[1] "S3"

1.3 创建泛型函数

通常,我们使用 UseMethod() 来创建一个泛型函数,例如

person <- function(x, ...) {
  UseMethod('person')
}

定义完泛型函数之后,可以使用以下方式

  • person.xxx 定义名为 xxx 的方法
  • person.default 定义默认方法
person.default <- function(x, ...) {
  print("I am human.")
}

person.sing <- function(x, ...) {
  print("I can sing")
}

person.name <- function(x, ...) {
  print(paste0("My name is ", x))
}

那如何调用这些方法呢?

首先,我们定义一个 class 属性为 "sing" 的变量

> a <- structure("tom", class='sing')

然后,将该对象 a 传入 person

> person(tom)
[1] "I can sing"
> person.sing(a)
[1] "I can sing"

可以看到,调用了 person.sing() 方法。

让我们再尝试其他类型

> b <- structure("tom", class='name')
> person(b)
[1] "My name is tom"
> person("joy")
[1] "I am human."

这样,我们只要使用 person 函数,就能够对不同类型的输入做出相应,输入不同类型的对象会自动调用相应的方法。

对于未指定的类型,会调用 person.default 方法。这就是泛型函数。

1.4 S3 对象的方法

我们可以使用 methods() 函数来获取 S3 对象包含的所有方法

> methods(person)
[1] person.default person.name    person.sing  

可以使用 generic.function 参数,传递想要查询的泛型函数

> library(magrittr)
> methods(generic.function = print) %>% head()
[1] "print.acf"     "print.anova"   "print.aov"     "print.aovlist"
[5] "print.ar"      "print.Arima"

class 参数指定类名

> methods(class = lm) %>% head()
[1] "add1.lm"                   "alias.lm"                 
[3] "anova.lm"                  "case.names.lm"            
[5] "coerce,oldClass,S3-method" "confint.lm"

注意:一些输出的函数名后缀有 * 号表示不可见函数,例如

> print.xtabs
错误: 找不到对象'print.xtabs'

可以使用 getAnywhere 获取

> getAnywhere(print.xtabs)
A single object matching ‘print.xtabs’ was found
It was found in the following places
  registered S3 method for print from namespace stats
  namespace:stats
with value


function (x, na.print = "", ...) 
{
    ox <- x
    attr(x, "call") <- NULL
    print.table(x, na.print = na.print, ...)
    invisible(ox)
}
<bytecode: 0x7fe1e612b9e8>
<environment: namespace:stats>

或者 getS3method

> getS3method("print", "xtabs")
function (x, na.print = "", ...) 
{
    ox <- x
    attr(x, "call") <- NULL
    print.table(x, na.print = na.print, ...)
    invisible(ox)
}
<bytecode: 0x7fe1e612b9e8>
<environment: namespace:stats>

1.5 S3 对象的继承

S3 对象是通过 NextMethod() 方法继承的,让我们先定义一个泛型函数

person <- function(x, ...) {
  UseMethod('person')
}

person.father <- function(x, ...) {
  print("I am father.")
}

person.son <- function(x, ...) {
  NextMethod()
  print("I am son.")
}

执行

> p1 <- structure(1,class=c("father"))
> person(p1)
[1] "I am father."
> p2 <- structure(1,class=c("son","father"))
> person(p2)
[1] "I am father."
[1] "I am son."

可以看到,在调用 person(p2) 之后,会先执行 person.father() 然后执行 person.son()

注意:需要将被继承的类型放在第二个(son 之后)

> ab <- structure(1, class = c("father", "son"))
> person(ab)
[1] "I am father."

这样就实现了面向对象编程中的继承

1.6 缺点

  1. S3 并不是完全的面向对象,而是基于泛型函数模拟的面向对象
  2. S3 用起来简单,但是对于复杂的对象关系,很难高清对象的意义
  3. 缺少检查,class 属性可以被任意设置

1.7 示例

S3 对象系统广泛存在于 R 语言的早期开发中,因此,在 base 包中包含了许多 S3 对象。

例如

> ftype(plot)
[1] "S3"      "generic"
> ftype(print)
[1] "S3"      "generic"

自定义 S3 对象

say <- function(x, ...) {
  UseMethod("say")
}

say.numeric <- function(x, ...) {
  paste0("the number is ", x)
}

say.character <- function(x, ...) {
  paste0("the character is ", x)
}

使用

> say('nam')
[1] "the character is nam"
> say(12315)
[1] "the number is 12315"

相关文章

  • 生信课程笔记6-R语言简介

    R语言是一种面向对象的编程(object-oriented programming,OOP)。面向对象的编程就是在...

  • R 面向对象编程(一)

    前言 1. 面向对象 面向对象编程(object-oriented programming,OOP)是一种编程范式...

  • python-day14

    一、面向对象编程 编程思想:1.面向对象编程 --> 算法,逻辑2.函数式编程 --> 函数3.面向对象编程 ...

  • 面向对象_初识

    目录 面向对象编程介绍 类与对象介绍 私有属性与私有方法 面向对象编程 1. 面向对象编程介绍 面向对象编程:Ob...

  • 2017-08-14

    面向对象编程用对象的思想去写代码,就是面向对象编程-面向过程-面向对象面向对象编程的特点1.抽象 抽取一样的东西...

  • 面向对象编程,类和对象

    面向对象编程 Java是面向对象的一门编程语言,所以余姚使用者具备面向对象编程的思想。 那么,什么是面向对象编程呢...

  • Swift和OC的区别

    一、编程范式 Swift可以面向协议编程、面向函数编程、面向对象编程。 OC主要是面向对象编程。 二、类型安全 S...

  • 谈谈面向对象编程

    何为面向对象编程 面向对象编程简介 面向对象编程(Object-oriented Programming,缩写:O...

  • R 面向对象编程(二)

    S4 2.1 介绍 S4 是标准的 R 语言面向对象实现方式,比 S3 的定义更加严格,S4 对象有专门的函数用于...

  • swift注意事项

    一、编程范式 Swift 可以面向协议编程(POP)、函数式编程、面向对象编程。 Object-C 以面向对象编程...

网友评论

      本文标题:R 面向对象编程(一)

      本文链接:https://www.haomeiwen.com/subject/gjaicltx.html