R 面向?qū)ο缶幊蹋ㄒ唬?/h1>

前言

1. 面向?qū)ο?/h3>

面向?qū)ο缶幊蹋?code>object-oriented programming,OOP)是一種編程范式,它將對(duì)象作為程序的基本單元查辩,一個(gè)對(duì)象包含了數(shù)據(jù)以及操作數(shù)據(jù)的函數(shù)骏全。

那什么是對(duì)象?對(duì)象是類(class)類的實(shí)例肪康。

那什么又是類呢荚恶?

類是對(duì)現(xiàn)實(shí)事物的抽象撩穿,比如說(shuō),人類是對(duì)世界上所有人的總稱谒撼,而你食寡、我卻是實(shí)實(shí)在在存在于現(xiàn)實(shí)中的,也就是一個(gè)個(gè)對(duì)象廓潜。

類的定義包含了對(duì)數(shù)據(jù)的描述以及對(duì)應(yīng)的操作方法抵皱,比如,人應(yīng)該有性別辩蛋、年齡呻畸、身高、體重等固有特征悼院,但是每個(gè)對(duì)象伤为,也就是說(shuō)雖然每個(gè)人的特征千差萬(wàn)別,但都有這些固定的屬性客觀存在的据途。

2. R 的面向?qū)ο缶幊?/h3>

之前绞愚,我們對(duì) R 的理解可能都是停留在函數(shù)式編程的概念里。也就是編寫一個(gè)個(gè)函數(shù)颖医,來(lái)處理不同的對(duì)象爽醋。

當(dāng)然,目前 R 主要用于統(tǒng)計(jì)計(jì)算便脊,而且代碼量一般不會(huì)很大蚂四,幾十或上百行。使用函數(shù)式的編程方式就可以很好的完成編程任務(wù)哪痰。

一般來(lái)說(shuō)遂赠,在 R 中,函數(shù)式編程要比面向?qū)ο缶幊讨匾枚嗌谓埽驗(yàn)槟阃ǔJ菍?fù)雜的問(wèn)題分解成簡(jiǎn)單的函數(shù)跷睦,而不是簡(jiǎn)單的對(duì)象。

那為什么我還要學(xué)習(xí)面向?qū)ο缶幊棠兀?/p>

面向?qū)ο缶幊痰膬?yōu)勢(shì)是肋演,能夠使程序便于分析抑诸、設(shè)計(jì)、理解爹殊,提高重用性蜕乡、靈活性和可擴(kuò)展性。

R 中的 OOP 系統(tǒng)

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

  • CRAN 包提供的:R6梗夸、R.oo层玲、proto

S3

1.1 概念

S3 面向?qū)ο缶幊蹋?R 中第一個(gè)也是最簡(jiǎn)單的 OOP 系統(tǒng),廣泛存在于早期開發(fā)的 R 包中辛块,也是 CRAN 包最常用的系統(tǒng)畔派。

S3 的實(shí)現(xiàn)是基于一種特殊的函數(shù)(泛型函數(shù),根據(jù)傳入對(duì)象的類型來(lái)決定調(diào)用哪個(gè)方法)

1.2 創(chuàng)建 S3 對(duì)象

注意:下面我們會(huì)使用 sloop 包提供的函數(shù)來(lái)幫助我們查看對(duì)象的類型

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

首先润绵,我們使用 attr 來(lái)創(chuàng)建一個(gè) S3 對(duì)象

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

使用 classattr 獲取對(duì)象的類型

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

再用 sloop 包的 otype 來(lái)判斷是何種對(duì)象

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

我們也可以使用 structure 來(lái)構(gòu)建一個(gè) S3 對(duì)象

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

還可以使用為 class(var) 賦值的方式構(gòu)建

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

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

還可以將類屬性設(shè)置為向量线椰,為 S3 對(duì)象指定多個(gè)類型

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

1.3 創(chuàng)建泛型函數(shù)

通常,我們使用 UseMethod() 來(lái)創(chuàng)建一個(gè)泛型函數(shù)尘盼,例如

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

定義完泛型函數(shù)之后士嚎,可以使用以下方式

  • person.xxx 定義名為 xxx 的方法
  • person.default 定義默認(rèn)方法
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))
}

那如何調(diào)用這些方法呢?

首先悔叽,我們定義一個(gè) class 屬性為 "sing" 的變量

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

然后,將該對(duì)象 a 傳入 person

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

可以看到爵嗅,調(diào)用了 person.sing() 方法娇澎。

讓我們?cè)賴L試其他類型

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

這樣,我們只要使用 person 函數(shù)睹晒,就能夠?qū)Σ煌愋偷妮斎胱龀鱿鄳?yīng)趟庄,輸入不同類型的對(duì)象會(huì)自動(dòng)調(diào)用相應(yīng)的方法。

對(duì)于未指定的類型伪很,會(huì)調(diào)用 person.default 方法戚啥。這就是泛型函數(shù)。

1.4 S3 對(duì)象的方法

我們可以使用 methods() 函數(shù)來(lái)獲取 S3 對(duì)象包含的所有方法

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

可以使用 generic.function 參數(shù)锉试,傳遞想要查詢的泛型函數(shù)

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

class 參數(shù)指定類名

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

注意:一些輸出的函數(shù)名后綴有 * 號(hào)表示不可見(jiàn)函數(shù)猫十,例如

> print.xtabs
錯(cuò)誤: 找不到對(duì)象'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 對(duì)象的繼承

S3 對(duì)象是通過(guò) NextMethod() 方法繼承的,讓我們先定義一個(gè)泛型函數(shù)

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

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

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

執(zhí)行

> 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."

可以看到呆盖,在調(diào)用 person(p2) 之后拖云,會(huì)先執(zhí)行 person.father() 然后執(zhí)行 person.son()

注意:需要將被繼承的類型放在第二個(gè)(son 之后)

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

這樣就實(shí)現(xiàn)了面向?qū)ο缶幊讨械睦^承

1.6 缺點(diǎn)

  1. S3 并不是完全的面向?qū)ο螅腔诜盒秃瘮?shù)模擬的面向?qū)ο?/li>
  2. S3 用起來(lái)簡(jiǎn)單应又,但是對(duì)于復(fù)雜的對(duì)象關(guān)系宙项,很難高清對(duì)象的意義
  3. 缺少檢查,class 屬性可以被任意設(shè)置

1.7 示例

S3 對(duì)象系統(tǒng)廣泛存在于 R 語(yǔ)言的早期開發(fā)中株扛,因此尤筐,在 base 包中包含了許多 S3 對(duì)象。

例如

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

自定義 S3 對(duì)象

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"
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末洞就,一起剝皮案震驚了整個(gè)濱河市盆繁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旬蟋,老刑警劉巖改基,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡秕狰,警方通過(guò)查閱死者的電腦和手機(jī)稠腊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鸣哀,“玉大人架忌,你說(shuō)我怎么就攤上這事∥页模” “怎么了叹放?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)挠羔。 經(jīng)常有香客問(wèn)我井仰,道長(zhǎng),這世上最難降的妖魔是什么破加? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任俱恶,我火速辦了婚禮,結(jié)果婚禮上范舀,老公的妹妹穿的比我還像新娘合是。我一直安慰自己,他們只是感情好锭环,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布聪全。 她就那樣靜靜地躺著,像睡著了一般辅辩。 火紅的嫁衣襯著肌膚如雪难礼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天玫锋,我揣著相機(jī)與錄音鹤竭,去河邊找鬼。 笑死景醇,一個(gè)胖子當(dāng)著我的面吹牛臀稚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播三痰,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼吧寺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了散劫?” 一聲冷哼從身側(cè)響起稚机,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎获搏,沒(méi)想到半個(gè)月后赖条,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年纬乍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碱茁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仿贬,死狀恐怖纽竣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情茧泪,我是刑警寧澤蜓氨,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站队伟,受9級(jí)特大地震影響穴吹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嗜侮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一港令、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棘钞,春花似錦、人聲如沸干毅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)硝逢。三九已至姨拥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渠鸽,已是汗流浹背叫乌。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徽缚,地道東北人憨奸。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像凿试,于是被迫代替她去往敵國(guó)和親排宰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容