變量用於臨時存儲數據,而函數用於操作數據,實現代碼的重復使用。在R中,函數只是另一種數據類型的變量,可以被分配,操作,甚至把函數作為參數傳遞給其他函數。分支控制和循環控制,和通用編程語言的風格很相似,但是,不要因為R具有這些元素,就把R作為通用編程語言來看待,R的最小變量是向量,是一種面向數組(Array-Oriented)的語言。在編程時,盡量用array的方式思考,避免使用循環(for,while,repeat)控制,而使用apply函數家族實現計算的迭代,這是R語言的特色,把特定的函數應用於向量,列表或數組中的每個元素上。
一,R函數的風格
R通過function關鍵字定義函數,函數主要由函數名稱,參數,運行的代碼塊和返回值組成,函數名稱是變量,參數是調用函數時需要傳遞的形式參數;代碼塊是由由大括號構成,是調用函數時需要執行的代碼邏輯;R的函數不需要顯式地使用return關鍵字明確返回值,R函數的計算的最后一個值將自動作為返回值。
1,創建函數
例如,創建一個函數,並把函數賦值給avg變量,可以認為函數的名稱是avg,其有兩個參數a和b,大括號內的代碼是函數體,實現的功能是求兩個數的平均值。在R中創建函數時,如果函數體只有一行代碼,可以省略大括號。
avg <- function(a,b)
{
mead(c(a,b))
}
該函數沒有顯式調用return關鍵字,函數代碼塊種計算的最后一次自動最為返回值,在avg函數種,代碼塊調用系統函數mean,返回參數a和b的平均值,調用格式是函數名稱(形式參數):
> avg(2,3) [1] 2.5
調用函數時,如果不命名參數,則R按照位置匹配參數,因此,2對應形式參數a,3對應形式參數b。如果要改變傳遞參數的順序,則可以傳入命名參數:
> avg(b=2,a=3) [1] 2.5
可以為函數的參數設置默認值,當調用函數時,如果沒有為參數傳遞相應的值,那么函數將自動使用默認值作為函數代碼塊執行的當前值。
> avg <- function(a=1,b=1)mean(c(a,b)) > avg() [1] 1
雖然函數中最后一行代碼的值是自動返回的,但是,為了便於閱讀代碼,建議使用return函數,清晰地指定要返回的值,return函數除了指定函數的返回值之外,還能使函數退出,不再繼續執行后面的代碼。
> avg <- function(a=1,b=1) return(mean(c(a,b)))
2,把函數作為參數傳遞給其他函數
函數本質上是一個函數類型的變量,和其他類型的變量一樣,能夠作為函數的形式參數,傳遞給其他函數,例如:
> fn_call <- function(x,a,b)x(a,b) > fn_call(avg,1,2) [1] 1.5
R提供一個系統函數do.call(fun,args),用於調用其他函數,第一個參數是函數變量,第二個參數是有參數構成的列表:
> do.call(avg,args=list(1,2)) [1] 1.5
3,查看函數的定義
通過函數args用於顯示函數的頭部文本,用於顯示函數的頭部定義,例如:
> args(avg) function (a = 1, b = 1) NULL
body(fun)函數用於返回函數的代碼塊,例如:
> body(avg)
mean(c(a, b))
除了這兩個函數,還有函數formals(fun),用於查看函數的參數及其默認值,如果函數沒有定義參數的默認值,那么只顯示參數名,formals顯示的參數名格式是$參數名。函數deparse(fun)用於查看函數調用的函數。
4,特殊的參數
R提供了一個特殊的運算符(...),該運算符允許函數具有任意多個的參數,並且不需要在函數定義中指定。
avg <- function(a,b, ...) mead(c(a,b))
二,分支控制
分支和循環是通用編程語言中必不可少的兩大流程控制元素,分支控制是根據條件表達式的結果,執行不同的代碼段。
1,if-else控制
經典的流程控制關鍵字是if-else,並可以把多個if-else語句連接到一起:
if ( test_expression1) { statement1 } else if ( test_expression2) { statement2 } else { statement3 }
2,ifelse控制
ifelse控制是,在一個函數中,結合了if-else的功能,其語法是:
ifelse(test, yes, no)
當test條件為TRUE時,返回yes表達式的值,當test條件為FALSE時,返回no表達式的值。
> ifelse(c(1,2,3)>=2,'Greater','Less') [1] "Less" "Greater" "Greater"
對於ifelse的返回值,官方文檔的描述是:
A vector of the same length and attributes (including dimensions and "class") as test and data values from the values of yes or no. The mode of the answer will be coerced from logical to accommodate first any values taken from yes and then any values taken from no.
注意:返回值的class屬性跟test表達式相同,其mode屬性是由 yes 或 no表達式確定的。
當ifelse()用於返回Date類型的對象時,返回值是numeric類型,而不是Date類型,這是因為返回值的class是由test表達式決定的。
dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05')) dates <- ifelse(dates == '2011-01-01', dates - 1, dates)
解決方案是:把返回值的class重新設置為Date類型。
dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05')) dates <- ifelse(dates == '2011-01-01', dates - 1, dates) class(dates) <- 'Date'
3,switch函數
如果分支較多,可以使用switch函數實現分支的選擇,switch函數的第一個參數是表達式(exp),通常是一個字符串;當表達式(exp)匹配后續的參數名(即變量名)時,返回參數的值:
> color <- function(t) switch(t,r='red',g='green',b='blue') > color('r') [1] "red"
如果不匹配任何參數名,switch函數不返回任何值,可以添加一個匿名的參數,當表達式(exp)匹配不上任意一個命名參數時,switch函數將返回匿名參數的值:
> color <- function(t) switch(t,r='red',g='green',b='blue', 'error') > color('d') [1] "error"
三,循環控制
循環控制是根據條件重復執行代碼塊,為了避免無限循環,可以根據條件結束循環。循環控制語句是repeat、while和for,R的向量化自帶循環特性,減少了循環語句使用的場景,但是在重復執行代碼時,循環控制還是非常有用。
1,repeat 循環
repeat 循環:先執行代碼,遇到break關鍵字,結束循環,也可以在break關鍵字前增減if(test)語句,當指定的條件成立(為TRUE)時,執行break關鍵字,結束循環:
repeat { code if(test) break }
2,while 循環
while 循環:先檢測條件,如果條件為TRUE,執行code;如果條件為FALSE,結束循環:
while(test) { code }
3,for 循環
for 循環:使用迭代器和一個向量參數,在每個循環中,迭代器變量從向量中取得一個值,直到迭代所有得向量:
for(i in c(1:5)) { code }
4,向量化的循環
R是向量化的編程語言,這使得R自身帶有循環的特性,而不用使用循環控制語句。關於向量化的循環控制,請閱讀我的博客文章
參考文檔:
How to prevent ifelse() from turning Date objects into numeric objects