<span style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 1.25; background-color: rgb(255, 255, 255);">譯自:</span>
http://adv-r.had.co.nz/Functions.html
“To understand computations in R, two slogans are helpful:
- Everything that exists is an object.
- Everything that happens is a function call."
— John Chambers
所有均爲對象,包括函數;
函數
- formals,形參表,可用formals(fun)查看;
- body,函數體,可用body(fun)查看;
- environment,環境,可用environment(fun)查看;
Primitive函數,該類型函數直接使用.Primitive()調用同名C函數;
> sum
function (..., na.rm = FALSE) .Primitive("sum")
> formals(sum)
NULL
> body(sum)
NULL
> environment(sum)
NULL
formal <- sapply(funs,FUN=formals);
len <- sapply(formal,FUN=length);
# Q1: which base funtion has the most arguments?
> max(len);
[1] 22
> funs(len==22)
$scan
# Q2: how many base functions has no arguments? What's special baout these functions?
> sum(len==0)
[1] 221
# Q3: how could you adapt the code to find all primitive functions?
funs <- Filter(is.function,objs);
is_primitive <- function(x)
{
return(is.null(formals(x))&is.null(body(x))&is.null(environment(x)));
}
primitive_funs <- Filter(is_primitive,funs)
primitive_funs
靜態域
四條基本原則:
- name masking
- functions vs. variables
- a fresh start
- dynamic lookup
<pre name="code" class="plain"># 尋常局部<strong>變量</strong>
x <- 2
g <- function() {
y <- 1
c(x, y)
}
g()
rm(x, g)
</pre><pre name="code" class="plain"># <strong>變量包含在嵌套定義函數中</strong> <span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;">a function is defined inside another function</span>
x <- 1
h <- function() {
y <- 2
i <- function() {
z <- 3
c(x, y, z)
}
i()
}
h()
rm(x, h)
</pre><pre name="code" class="plain"># <strong>局部定義函數</strong>屏蔽上級函數
l <- function(x) x + 1
m <- function() {
l <- function(x) x * 2
l(10)
}
m()
#> [1] 20
rm(l, m)
</pre><pre name="code" class="plain"># <strong>closure</strong>
j <- function(x) {
y <- 2
function() {
c(x, y)
}
}
k <- j(1) # 返回函數
k() # 調用該函數
rm(j, k)
<span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;"># 注: 可能有點奇怪,調用完j後,返回函數k()是如何知道局部變量y的值?</span><span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;">. It works because </span><code style="box-sizing: border-box; font-family: Inconsolata, sans-serif; font-size: 14px; padding: 1px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; line-height: 20px; background-color: rgb(250, 250, 250);"><strong>k</strong></code><span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;"><strong> preserves the environment</strong> in which it was defined #-> and because the environment includes the value of </span><code style="box-sizing: border-box; font-family: Inconsolata, sans-serif; font-size: 14px; padding: 1px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; line-height: 20px; background-color: rgb(250, 250, 250);">y</code><span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;">. </span><a target=_blank href="http://adv-r.had.co.nz/Environments.html#environments" style="box-sizing: border-box; color: rgb(66, 139, 202); text-decoration: none; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;">Environments</a> <span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;">gives some pointers on how you can dive in and figure out what values are #-> stored in the environment associated with each function.</span>
`(` <- function(e1) {
if (is.numeric(e1) && runif(1) < 0.1) {
e1 + 1
} else {
e1
}
}
replicate(50, (1 + 2))
#> [1] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 3 4 3 3 3 3 3 4 3 3 3 3 3 3 3 4 3 3 3
#> [36] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
rm("(")
每次調用函數均是開闢一個新的空間。查看以下實例:
j <- function() {
if (!exists("a")) {
a <- 1
} else {
a <- a + 1
}
print(a)
}
j()
rm(j)
此函數每次調用均返回1!因爲每調用完一次,資源就會被收回,當次調用不會知道上次調用結果a。f <- function() x
x <- 15
f()
#> [1] 15
x <- 20
f()
#> [1] 20
問題出在:R looks for values when the function is run, not when it’s created.f <- function() x + 1
codetools::findGlobals(f)
解決這種問題的一種極端方法是將每次調用f的新環境強制定義爲空環境;environment(f) <- emptyenv()
f()
#> Error: could not find function "+"
Every operation is a function call
Note that `(Esc下面那個鍵,類似於Shell裏面eval操作符), the backtick, lets you refer to functions or variables that have otherwise reserved or illegal names:x <- 10; y <- 5
x+y 等價於 `+`(x,y)
</pre><pre name="code" class="plain">for (i in 1:2) print(i) 等價於 `for`(i,1:2,print(i))
</pre><pre name="code" class="plain">if(i==1) print("yes") else print("no") 等價於 if(i==1,print("yes"),print("no"))
</pre><pre name="code" class="plain">x[3] 等價於 `[`(x,3)
</pre>比較常用的地方是sapply/lapply等</div><div><span style="color:#333333;"><span style="line-height: 20px;"><span style="font-size: 14px;"></span></span></span><pre name="code" class="plain">add <- function(x, y) x + y
sapply(1:10,add, 3)
等價於
sapply(1:10,`+`,3)
等價於
sapply(1:10,"+",3)
最後一個可行的原因在於<code style="box-sizing: border-box; font-family: Inconsolata, sans-serif; font-size: 14px; padding: 1px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; line-height: 20px; background-color: rgb(250, 250, 250);">lapply() source code</code><span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;">, you’ll see the first line uses </span><code style="box-sizing: border-box; font-family: Inconsolata, sans-serif; font-size: 14px; padding: 1px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; line-height: 20px; background-color: rgb(250, 250, 250);">match.fun()</code><span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;"> to find functions given their names.</span>
</pre><h3 style="box-sizing: border-box; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-weight: 500; line-height: 1.1; margin-top: 20px; margin-bottom: 10px; font-size: 24px; color: rgb(51, 51, 51);">Calling a function given a list of arguments</h3>當參數較長時,我們可以調用do.call</div><div><span style="color:#333333;"><span style="line-height: 20px;"><span style="font-size: 14px;"></span></span></span><pre name="code" class="plain">args <- list(1:10, na.rm = TRUE);
do.call(mean,args);
Default and missing arguments
A) missing()函數可以驗證是否提供參數i <- function(a, b) {
c(missing(a), missing(b))
}
i()
#> [1] TRUE TRUE
i(a = 1)
#> [1] FALSE TRUE
i(b = 2)
#> [1] TRUE FALSE
i(1, 2)
#> [1] FALSE FALSE
Lazy evaluation
所謂“Lazy”是指,函數形參只有在使用的時候纔會eval,如,f <- function(x) {
10
}
f(stop("This is an error!"))
#> [1] 10
如果想保證x被eval,那麼可以使用force()函數,如f <- function(x)
{
force(x);
10;
}
add <- function(x) {
function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
#> [1] 20
adders[[10]](10)
#> [1] 20
因爲在使用lapply創建closure時,x沒有被eval,只有在第一次調用時,才eval x,此時x值是10;add <- function(x) {
force(x); # eval x every time create a closure
function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
#> [1] 11
adders[[10]](10)
#> [1] 20
這種Lazily evaluation的好處有(一般是短路技巧):
# 1
`&&` <- function(x,y)
{
if(!x) return(FALSE);
if(!y) return(FALSE);
return(TRUE);
}
x <- NULL;
if(!is.null(x)&&x>0) # if and only if x is not NULL
{print("yes")}
# 2
if(is.null(x)) stop("a is null")
等價於
!is.null(x)||stop("x is null")
Special calls
# R 預定的prefix function有:
%%, %*%, %/%, %in%, %o%, %x%.
# The complete list of built-in infix operators that don’t need % is:
::, :::, $, @, ^, *, /, +, -, >, >=, <, <=, ==, !=, !, &, &&, |, ||, ~, <-, <<-
replacement functions
`second<-` <- function(x,value)
{
x[2] <- value;
return(x);
}
second(x) <- 3;
等價於
x=`second<-`(x,3);
> address(x)
[1] "0x15b70688"
> second(x)<-3
> address(x)
[1] "0x144ac9c8"
R語言還有一些內置的replacement function,如[]等,可以作爲左值的函數。Return values
f1 <- function() return(invisible(1));
> f1()
B) 調用on.exit保證,不管函數時正常還是非正常退出,都可以執行某一操作in_dir <- function(dir, code) {
old <- setwd(dir)
on.exit(setwd(old)) # 而on.exit當且僅當函數退出時纔會執行。
force(code)
}
getwd()
#> [1] "/home/travis/build/hadley/adv-r"
in_dir("~", getwd())
#> [1] "/home/travis"