Functions
一些基本的函式概念可參考網路上程式設計的入門教學,這裡主要介紹R中函式跟C語言比起來不一樣的地方,在一些動態語言(R、javascript...)中函式被視為First-class object,簡單來說可以把函式視為一個物件來看待(實際上函式也屬於function類別),但是特性又有微妙不同,First class object有以下特性,前兩點比較好理解,就是從物件的觀點去看函式,後兩點有興趣再去Google就好:
- 可被存入變數或其他結構(ex:vector, list...)
- 可作為函式的引數、返回值
- 可在執行期動態創造
- 不與特定名稱繫結也可存在(例如所謂的匿名函數)
函式宣告
因為R語言將函式被當作物件看待,所以可將函式指定一個變數名稱:
> f <- function(){
+ #do something...
+ }
函式執行
跟其他語言一樣是在函式名稱後面加(),如果函式有返回值可以指定給其他變數,新手需要注意是"執行後以函式返回值對變數賦值"或"定義函式指定給變數",兩者是不一樣的。
#假設f有返回值"abc",若無明確指定給變數就會導向stdout (console)
> f()
[1] "abc"
#這個動作是拿返回值"abc"給變數c
> c <- f()
> c
[1] "abc"
#c不是函式,只是"abc"
> c()
NULL
Parameter / Argument
參數(Parameter)和引數(Argument)兩者之間時常被搞混,所謂參數是指函式在參數式定義的東西,而引數是指外部餵給參數式的資料,不過通常搞混也沒啥差...
> f <- function(arg){
+ arg + 1
+ }
> f(1)
[1] 2
#a (char)無法+1 (numeric),會發生錯誤。
> f("a")
Error in arg + 1 : non-numeric argument to binary operator
Lazy evaluation
R語言具有Lazy evaluation特性,就是說函式在語句中真正使用到引數時才會去檢查引數的型別是否正確,因此函式參數的定義是所謂的鴨子型別(ducky typing),這裡就衍伸了兩個注意的點:
需要注意傳入的引數型別在函式內可被正確使用。
> f <- function(arg){ + arg + 1 + } #arg要的是numeric,卻給他character,就會發生錯誤。 > f("a") Error in arg + 1 : non-numeric argument to binary operator
在函式執行時沒有被呼叫的參數可以為空。
> f <- function(a,b){ + print(a) + } #由於上面沒有用到b,所以不給定b值執行f不會有錯誤 > f(1) [1] 1 > f <- function(a,b){ + print(a); + print(b); + } #由於上面沒有用到b,所以不給定b值執行f的時候找不到b,報錯 > f(1) [1] 1 Error in print(b) : argument "b" is missing, with no default
default value
參數可以有預先設定的值,如此一來在呼叫函式的時候就可以不需要傳入引數給該參數,反之不具有default value的參數都必須要給引數,不然會發生錯誤。
> f <- function(arg){
+ arg + 1
+ }
#因為arg沒有default value,所以必須要明確定義參數arg才可執行
> f(1)
[1] 2
> f()
Error in f() : argument "arg" is missing, with no default
#把arg指定為1,就不必傳入引數定義參數arg
> f <- function(arg=1){
+ arg + 1
+ }
> f()
[1] 2
matching
引數可能不只一個,這時候就需要把傳入的引數對應到參數,R語言依照這些順序判斷引數:
matched by name explicitly
找是否有名稱一樣的參數。
matched by name partially
找是否有名稱部分一樣的參數。不建議用這個做法...參數名子背不起來就用R studio吧,珍惜生命去看提示。
matched by position
按照引數填入的位子去對應參數。要注意不具有default value的參數都必須要給引數,不然會發生錯誤,所以在matched by position的情況下如果要跳過第一個有預設值的參數,把該位置空著就是了,可以參考下列的
f(,"abc")
,後面第三個參數就不需要理會
> f <- function(argument="argument", arg="arg", char="char"){
+ print(argument)
+ print(arg)
+ }
#會先找有沒有跟arg完全同名的參數,把引數"test"套進去
> f(arg="test")
[1] "argument"
[1] "test"
#因為找不到跟argu完全同名的參數,所以找名子裡面含有argu的參數套引數"test"
> f(argu ="test")
[1] "test"
[1] "arg"
#match by position的做法,跳過第一、三個具有預設值的參數
> f(,"abc")
[1] "argument"
[1] "abc"
... argument
參數...
出現在參數列,代表該位置可接受未確定數量的多個引數,依照argument matching的規則,可以分成兩種較常用的情況:
...
在參數列最後面,通常用在函式之間的相互嵌套,看懂了嗎^^:
> a <- function(...){
+ print(paste(...))
+ }
> a("a", "b", "c")
[1] "a b c"
#透過...把很長的引數向巢狀函式內部傳遞,而不用把內部函式需要的引數再全部重打一次。
> b <- function(arg1=1, arg2=2, ...){
#can do something use arg1 arg2
#or you can pass them to a() if needed
+ a(...)
+ }
> b("a", "b", "c")
[1] "a b c"
...
在參數列最前面,這時候對於...
之後的引數必須要明確指定參數名子,並且不可用partial matching指定參數,否則都會被當作...
引數的一部分。
#先查看paste()接受的參數
> args(paste)
function (..., sep = " ", collapse = NULL)
NULL
#如果要指定在...之後的sep參數,必須要明確寫出參數名稱
> paste("a", "b", sep=",")
[1] "a,b"
#如果不寫出名稱會被當作...的一部分
> paste("a", "b", ",")
[1] "a b ,"
#前面有了...,後面的參數名稱就不接受partial matching
> paste("a", "b", se=",")
[1] "a b ,"
Return value
函式的返回值是指函式執行後,除了執行內部語句外會回傳給你什麼值,R語言中函式默認以函式底部的求值表達式作為返回值,如果明確在函式中間定義返回值,則當函式返回一個值時此函式結束剩餘的語句不會執行。
> f <- function(){
+ #do something
+ "abc"
+ }
> f()
[1] "abc"
Generator
可以透過把函式放在控制結構裡面,實作模仿某些語言所謂的Generator,有興趣可Google,這裡等我實際用到再回來寫吧...