R 學習筆記:R 函數

既然選擇用 R 語言了, 還是得了解一下 R 語言的磚塊, 函數是怎麼處理的吧? R 的函數還是很神奇的.

R 中的許多對數據的處理需要用函數來完成, 要麼使用別人編寫好的函數, 要麼使用自己去寫的函數. 所以, 知道 R 語言中的函數是什麼, 該怎麼用是十分有必要的.

R 語言中的函數, 有的使用 R 語言去寫的, 有的則考慮到性能等原因使用 C/C++ 等語言來寫. 使用非 R 語言寫的函數和 R 語言寫的函數是有差別的. 下面主要考慮 R 語言編寫的函數吧.


函數結構

一個 R 函數有三個部分: 函數體 (body); 參數 (formals); 環境 (environment). 對於一個函數, 可以使用 body(), formals(), environment() 等函數可以去查看函數的這些部分.

對象查找

函數中的對對象查找是有一定的順序的. R 語言中的名稱域查找有兩類: 詞法域 (lexical scoping), 默認查找的方式; 動態域 (dynamic scoping), 在互動式操作的運行中可以減少輸入.

詞法域 (lexical scoping) 查找有四個基本概念.

  • 名稱覆蓋 (name masking): 首先查找函數體內的對象是否有符合對應名稱的變數, 如果沒有的話, 就再上升一個層次繼續查找.
  • 方程和變數 (functions v.s. variables): 如果方程和變數的名稱一樣的話, 會根據當時的語法去判斷是否是方程或變數.
  • 起始 (a fresh start): 函數每次運行, 其中的變數都是重新建立的, 和上次運行沒有關係.
  • 動態查找 (dynamic lookup): R 語言中, 對變數的查找是在函數運行的時候進行的, 而不是在建立的時候建立的.

參數解析

R 函數對於參數的解析是很方便的. R 函數的參數可以按照順序進行分配, 也可以通過參數名稱進行指定. 所有的參數會把直接通過參數名賦值的參數進行對應賦值, 然後剩下的沒有通過參數名賦值的, 按照順序排列, 通過按順序賦值的方式進行賦值.

myfunc1 <- function(a, b, c) { print(a) print(b) print(c) }nmyfunc1(1, 2, b = 3)n#[1] 1n#[1] 3 n#[1] 2n

R 函數還可以指定默認值. 直接在參數表中加上等號來設置默認參數. 需要注意的是, 有默認值的參數也會按照順序進行賦值, 並不會跳過有默認值的參數.

myfunc2 <- function(a, b = 3) { print(a) print(b) }nmyfunc2(1)n#[1] 1 n#[1] 3n

可以通過 missing() 函數來判斷某個參數是否缺失.

myfunc3 <- function(a, b) { c(missing(a), missing(b)) }nmyfunc3(b = 1) n#[1] TRUE FALSEn

另外只有用到的參數才會實際上解析, 沒有用到的參數就不會解析, 這樣也不會耗費時間. 如果強制對某個沒有用到的參數進行解析, 可以使用 force 函數.

需要提及的是 ... 參數. 這個參數可以匹配任何沒有被匹配的參數, 並可以方便地傳遞到其他函數. 但是這個參數並不會去驗證正確性, 所以即是有打字錯誤, 外部函數並不會報錯.

myfunc4 <- function(...) { names(list(...)) }nmyfunc4(a = 1, b = 2) n#[1] "a" "b"n

返回函數

如果沒有使用 return 函數去指定函數的返回值, 則默認返回函數體中最後一條執行的命令的結果. 儘管函數只能返回一個結果, 但這個結果可以是一個數值, 可以是一個列表, 也可以是一個函數, 所以實際上一個函數可以返回符合需要的值.

如果不希望返回值被列印, 可以使用 invisable 函數. invisable 的結果可以被賦值, 但不會列印在屏幕, 不過總可以通過把其值包含在小括弧中來列印出來.

(invisable(2)) n#[1] 2 n(a <- 2) n#[1] 2n

on.exit 函數可以設置當一個函數結束時採取什麼動作. 其中可以是改變工作區間, 也可以刪除臨時變數等.

特殊函數

R 語言中有兩類特殊函數: 中置函數 (infix function), 替換函數 (replacement function).

中置函數

經常用到的很多函數都是前置的, 就是一個函數名, 然後後面是括弧和括弧裡面的參數. 有一些函數是中置的. R 中內部定義了一些: ::, $, @, ?, *, /, +, -, >, >=, <, <=, ==, !=, !, &, &&, |, ||, ~, <-, <<-. 自己定義的中置函數需要在前後加上百分號, 如一些已經定義好的函數: %%, %*%, %/%, %in%, %o%, %x%. 這些中置函數也可以像前置函數那樣使用, 不過需使用引號來標明函數名.

"%+%" <- function(a, b) npaste(a, b, sep = "")n"new" %+% "string" n#[1] "newstring" n`%+%`("new", " string") n#[1] "newstring"n

替換函數

替換函數有著特殊的名稱形式 xxx<-, 可以直接對內容進行替換. 常見的如 dim, names 等函數. 替換函數, 看上去是對變數內容的直接替換, 實際上在 R 裡面是新建了一個變數, 所以效率依然是不高的.

"myfunc5<-" <- function(x, value) {n x[2] <- valuen xn }nx <- 1:5nmyfunc5(x) <- 10nxn#[1] 1 10 3 4 5n


通過上文提到的 R 的特殊函數, 我們其實可以體會到, R 語言是很接近函數式編程語言, 甚至包括 R 裡面常用的 Reduce 函數 (有時間再說一下)就是從著名的函數式編程語言 lisp 中來的靈感.

其實啊, 筆者感覺 R 的函數是十分神奇的原因在於, 它有很多小技巧可以達成你希望的效果.

不像是 C 語言等傳統的通用編程語言, R 語言通過很多技巧可以達到靈活多變的效果, 方便各種數據分析場景. 不得不說, R 是一把數據分析的瑞士軍刀.


推薦閱讀:

Illumina測序數據的質量控制(QC)-1
轉錄組入門1-環境配置與軟體安裝
R 學習筆記:R 色彩
R 學習筆記: 數據輸入輸出

TAG:R编程语言 | 生物信息学 |