天天看點

R學習 第四篇:函數和流程控制

變量用于臨時存儲資料,而函數用于操作資料,實作代碼的重複使用。在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自身帶有循環的特性,而不用使用循環控制語句。關于向量化的循環控制,請閱讀我的部落格文章

R語言學習 第九篇:plyr包

R語言學習 第十篇:操作符

參考文檔:

How to prevent ifelse() from turning Date objects into numeric objects

作者

:悅光陰

出處

:http://www.cnblogs.com/ljhdo/

本文版權歸作者和部落格園所有,歡迎轉載,但未經作者同意,必須保留此段聲明,且在文章頁面醒目位置顯示原文連接配接,否則保留追究法律責任的權利。

繼續閱讀