R语言编程艺术(3)R语言编程基础
本文對(duì)應(yīng)《R語(yǔ)言編程藝術(shù)》
第7章:R語(yǔ)言編程結(jié)構(gòu);
第9章:面向?qū)ο蟮木幊?#xff1b;
第13章:調(diào)試
?
=========================================================================
R語(yǔ)言編程結(jié)構(gòu)
控制語(yǔ)句:
循環(huán):
for (n in x) { } while (condition) { } repeat { }
另外break也可以用在另兩種形式的循環(huán)語(yǔ)句中。注意repeat沒(méi)有跳出循環(huán)的判斷條件,因此使用break(或者類似return())的語(yǔ)句。
除此之外,next語(yǔ)句可以用來(lái)跳過(guò)本次迭代的剩余部分。具體應(yīng)用情景,是可以替代循環(huán)中的條件嵌套語(yǔ)句,不致代碼看起來(lái)混亂。
需要注意的是,for結(jié)構(gòu)可以用在任何向量上,無(wú)論向量是什么模式,注意靈活運(yùn)用。
?
對(duì)非向量集合的循環(huán):R并不支持直接對(duì)非向量集合的循環(huán),但是有一些間接但簡(jiǎn)單的方式可以做到這點(diǎn):
使用lapply()。如果循環(huán)的每次迭代之間相互獨(dú)立,就是用lapply(),可以允許以任意順序執(zhí)行;
使用get()。這個(gè)函數(shù)接受一個(gè)代表對(duì)象名字的字符串參數(shù),然后返回該對(duì)象的內(nèi)容,看起來(lái)簡(jiǎn)單,但是十分有用。
#假設(shè)u, v都是兩列的矩陣,要分別在這兩個(gè)矩陣上應(yīng)用lm函數(shù) for (m in c(“u”, “v”)) { z <- get(m) print(lm(z[, 2] ~ z[, 1])) }
if-else結(jié)構(gòu):?
#注意以下代碼 if (r == 4) {x <- 1 } else {x <- 3y <- 4 }
雖然if后面只有一條語(yǔ)句,但是它對(duì)應(yīng)的大括號(hào)是不能省略的,原因在于R的解釋器是根據(jù)else前面的右括號(hào)判斷這是一個(gè)if-else結(jié)構(gòu),如果沒(méi)有大括號(hào),R將認(rèn)為這是一個(gè)if結(jié)構(gòu)。
#if-else結(jié)構(gòu)的簡(jiǎn)化 v <- if (cond) expression1 else expression2
這是一個(gè)簡(jiǎn)化代碼的技巧,因?yàn)閕f-else結(jié)構(gòu)最終會(huì)返回的值取決于cond是否為真。如果expression是函數(shù)調(diào)用語(yǔ)句,可以讓代碼更為清晰,但是如果語(yǔ)句復(fù)雜,就要優(yōu)先考慮代碼是否清晰易懂。
處理向量時(shí),使用向量化的ifelse()函數(shù)有可能提高運(yùn)行速度。
?
?
算術(shù)和邏輯運(yùn)算符及數(shù)值:
之前提到,R中只有向量,沒(méi)有標(biāo)量,標(biāo)量可以看作是僅有一個(gè)值的向量。但是在邏輯運(yùn)算時(shí),特別是if語(yǔ)句的條件控制,其邏輯值不能為向量,只能有一個(gè)值,也就是可以看作標(biāo)量。這時(shí),就要區(qū)分邏輯與和邏輯或的向量標(biāo)量運(yùn)算了。
| x && y | 標(biāo)量的邏輯與運(yùn)算 |
| x || y | 標(biāo)量的邏輯或運(yùn)算 |
| x & y | 向量的邏輯與運(yùn)算 |
| x | y | 向量的邏輯或運(yùn)算 |
如果if語(yǔ)句里的控制條件里出現(xiàn)向量并且長(zhǎng)度大于1,R的處理方式是將向量的第一個(gè)元素作為控制條件返回結(jié)果,然后提示warning message
?
?
參數(shù)的默認(rèn)值:
具名實(shí)參(named argument):如果不使用默認(rèn)值,那么在調(diào)用函數(shù)時(shí)必須給出參數(shù)的具體名稱;
惰性求值(lazy evaluation)原則:除非有需要,否則不會(huì)計(jì)算一個(gè)表達(dá)式的值,也就是說(shuō)具名實(shí)參不一定會(huì)被使用。
?
?
返回值:
函數(shù)的返回值可以是任何R對(duì)象。通常為列表形式,但是如果有需要,甚至可以返回另一個(gè)函數(shù)。
一般來(lái)說(shuō),如果只在函數(shù)結(jié)尾返回值,要避免顯式地調(diào)用return()函數(shù)。如果想要將程序表達(dá)得更加清晰,在函數(shù)中間部分返回值的時(shí)候可以顯式地調(diào)用return()函數(shù)。
返回復(fù)雜對(duì)象:函數(shù)的返回值可以是另一個(gè)函數(shù)。如果函數(shù)需要返回多個(gè)返回值,可以把它們儲(chǔ)存在一個(gè)列表或其他容器變量中。
?
?
函數(shù)都是對(duì)象:
注意到函數(shù)是對(duì)象,也就是說(shuō)對(duì)于對(duì)象的操作對(duì)函數(shù)也適用。包括創(chuàng)建、編輯,甚至在以函數(shù)組成的列表上做循環(huán)。
g <- function(h, a, b) h(a, b) body(g) <- quote(2 * x + 3) g(3) # 9
首先,function函數(shù)創(chuàng)建了一個(gè)函數(shù),并賦值給g;然后body函數(shù)修改了g的函數(shù)體。因?yàn)楹瘮?shù)主體部分屬于”call”類,而這種類是由quote()函數(shù)生成。
?
環(huán)境和變量作用域的問(wèn)題:
頂層環(huán)境:<environment: R_GlobalEnv>
變量作用域的層次:里層的函數(shù)可以使用更加頂層的變量,如果層次之間變量命名發(fā)生沖突,優(yōu)先使用最里層的變量。一般來(lái)說(shuō),里層對(duì)于更加頂層的變量只讀不可寫。可寫的情況將單獨(dú)討論。
ls()函數(shù):調(diào)用不帶參數(shù)的ls()可以返回當(dāng)前的局部變量;使用envir參數(shù)可以返回函數(shù)調(diào)用鏈中任何一個(gè)框架的局部變量名。
函數(shù)代碼對(duì)于其非局部變量一般只讀不寫,即使重新賦值也是影響它們的備份而不是變量本身。事實(shí)上,直到局部變量發(fā)生變化前,與其對(duì)應(yīng)的全局變量是共享一個(gè)內(nèi)存地址的。
?
?
R語(yǔ)言中沒(méi)有指針:
不能直接改變一個(gè)函數(shù)的參數(shù),只能重新賦值。
其他因?yàn)闆](méi)有指針造成的問(wèn)題將在下文討論。
?
?
向上級(jí)層次進(jìn)行寫操作:
利用超賦值運(yùn)算符對(duì)非局部變量進(jìn)行寫操作:<<-
注意,超賦值運(yùn)算符進(jìn)行寫操作的時(shí)候,首先逐層向上級(jí)環(huán)境層次查找,遇到該變量的第一個(gè)層次即進(jìn)行操作,如果沒(méi)有遇到,將在全局層次上新建變量。
用assign()函數(shù)對(duì)非局部變量進(jìn)行寫操作:
可以通過(guò)設(shè)置函數(shù)參數(shù)指定向上層的哪一層次的變量進(jìn)行寫操作。注意引用變量是使用字符串實(shí)現(xiàn)的。
全局變量的使用:一般來(lái)說(shuō)不推薦在函數(shù)中寫全局變量,但是有時(shí)為了簡(jiǎn)化代碼,這么做也是可以的,靈活運(yùn)用即可。對(duì)于大型程序不推薦這樣做,因?yàn)榇嬖谥貙懖幌嚓P(guān)卻同名變量的風(fēng)險(xiǎn)。一種折中的方案是,在頂層環(huán)境中新建一個(gè)封裝全局變量的包,再使用assign()和get()函數(shù)通過(guò)索引到這個(gè)包來(lái)訪問(wèn)全局變量。
閉包:這里的閉包指一種編程方法,具體做法是,先定義一個(gè)可創(chuàng)建局部變量的函數(shù),再創(chuàng)建另一個(gè)函數(shù)可以訪問(wèn)該變量。這樣,后面的一些函數(shù)創(chuàng)建的局部變量之間互不影響,各自獨(dú)立。
?
?
遞歸:
用于回歸和分類的遞歸分塊方法庫(kù):rpart
?
?
置換函數(shù):
任何左邊不是標(biāo)識(shí)符(意味變量名)的賦值語(yǔ)句都可看作是“置換函數(shù)”。當(dāng)遇到以下形式:
g(u) <- v
R語(yǔ)言會(huì)嘗試用以下方式執(zhí)行:
u <- “g<-”(u, value = v)
直觀感受下,就是將值賦值給函數(shù)調(diào)用的結(jié)果。事實(shí)上,只要左邊不是變量名,就是一個(gè)置換語(yǔ)句,比如u[2] <- 8
?
?
寫函數(shù)代碼的工具:
快速載入環(huán)境:
?
source(“filename.R”)
?編輯函數(shù):
f1 <- edit(f1) f2 <- edit(f1)
?
創(chuàng)建自己的二元運(yùn)算符:
> “%a2b%” <- function(a, b) return(a + 2 * b) > 3 %a2b% 5 [1] 13
??
匿名函數(shù):
如果函數(shù)不是很復(fù)雜,可以省去函數(shù)定義的過(guò)程,在使用function()命令建立函數(shù)后直接調(diào)用,因?yàn)闆](méi)有具體的函數(shù)名,因此稱為匿名函數(shù)。應(yīng)用情況取決于具體情況,可以使代碼更加易讀緊湊。
?
?
=========================================================================
面向?qū)ο蟮木幊?/p>
?
?
S3類:
一個(gè)S3類包含一個(gè)列表,再附加上一個(gè)類名屬性和調(diào)度(dispatch)的功能。
?
S3泛型函數(shù):即同一個(gè)函數(shù)可以針對(duì)不同的類調(diào)用不同的操作。可以通過(guò)method()函數(shù)找到給定泛型函數(shù)的所有實(shí)現(xiàn)方法,返回值中,星號(hào)標(biāo)注的是不可見(jiàn)函數(shù)(nonvisible function),即不在默認(rèn)命名空間中的函數(shù),可以通過(guò)getAnywhere()函數(shù)找到這些函數(shù)。另外可以查看一個(gè)類能應(yīng)用的所有泛型函數(shù),代碼method(, class = “classname”)
?
編寫S3類:“類”屬性可以通過(guò)attr()或class()函數(shù)手動(dòng)設(shè)置。然后可以針對(duì)自己編寫的類,設(shè)定獨(dú)有的泛型函數(shù),下面是一個(gè)例子:
#創(chuàng)建一個(gè)名為employee的類的對(duì)象 j <- list(name = “Joe”, salary = 55000, union = T) class(j) <- “employee” #編寫類employee的打印方法 print.employee <- function(wrkr) {cat(wrkr$name, “\n”)cat(“salary”, wrkr$salary, “\n”)cat(“union member”, wrkr$union, “\n”) }
使用繼承:繼承的思想是在已有類的基礎(chǔ)上創(chuàng)建新的類:?
新類包含了兩個(gè)字符串,分別代表新類和原有的類,新類繼承了原有類的方法,可以使用對(duì)應(yīng)的泛型函數(shù)。泛型函數(shù)在調(diào)度時(shí)的工作原理是首先查找兩個(gè)類名稱的第一個(gè),如果沒(méi)查到,則再去查找第二個(gè)類,實(shí)現(xiàn)了繼承。
?
S4類:
| 操作 | S3類 | S4類 |
| 定義類 | 在構(gòu)造函數(shù)的代碼中隱式定義 | setClass() |
| 創(chuàng)建對(duì)象 | 創(chuàng)建列表,設(shè)置類屬性 | new() |
| 引用成員變量 | $ | @ |
| 實(shí)現(xiàn)泛型函數(shù)f() | 定義f.classname() | setMethod() |
| 聲明泛型函數(shù) | UseMethod() | setGeneric() |
?
編寫S4類:
> #定義類employee > setClass(“employee”, + representation( + name = “character”, + salary = “numeric”, + union = “l(fā)ogical”) + )[1] “employee”> #創(chuàng)建一個(gè)類的實(shí)例 > joe <- new(“employee”, name = “Joe”, salary = 55000, union = TRUE) > joeAn object of class “employee”Slot “name”:[1] “Joe”Slot “salary”:[1] 55000Slot “union”:[1] TRUE> #引用成員變量 > joe@salary[1] 55000> slot(joe, “salary”)[1] 55000> #賦值也可以使用這兩種方法 > joe@salary <- 88000 > slot(joe, “salary”) <- 88000
S4類的安全性在于,如果給不存在的成員變量賦值,會(huì)有報(bào)錯(cuò)消息,而S3類將會(huì)新建一個(gè)成員變量。
> joe@salry <- 48000Error in checkSlotAssignment(object, name, value) :“salry” is not a slot in class “employee”
在S4類上實(shí)現(xiàn)泛型函數(shù):
#show()函數(shù)指在交互模式下,輸入變量名直接打印 setMethod(“show”, “employee”, function(object) {inorout <- ifelse(object@union, “is”, “is not”)cat(object@name, “has a salary of”, object@salary,"and", inorout, "in the union", "\n") } )
S3類和S4類的對(duì)比:
S3具有較強(qiáng)的靈活性,而S4具有很好的安全性。一般來(lái)說(shuō)推薦使用S3使代碼更加靈活,而當(dāng)安全性更加重要時(shí)選擇S4(比如混合編程的情況下)。
?
?
對(duì)象的管理:
ls()函數(shù):列出所有對(duì)象;
rm()函數(shù):刪除特定對(duì)象;
save()函數(shù):保存對(duì)象集合,可以用load()載入;
查看對(duì)象內(nèi)部結(jié)構(gòu)的函數(shù):class(), mode(), names(), attributes(), unclass(), str(), edit();
exists()函數(shù):判斷對(duì)象是否存在。
?
?
=========================================================================
調(diào)試
調(diào)試的基本原則:
調(diào)試的本質(zhì):確認(rèn)原則;
從小處著手;
模塊化的、自頂向下的調(diào)試風(fēng)格;
反漏洞。
?
轉(zhuǎn)載于:https://www.cnblogs.com/gyjerry/p/6530099.html
總結(jié)
以上是生活随笔為你收集整理的R语言编程艺术(3)R语言编程基础的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 什么是压缩感知?[简单概括]
- 下一篇: [Leedcode][JAVA][第10