8.3 魔鬼(no.11~no.20)
8.3.11 integrate
中的非向量化函数
integrate
函数期待一个向量化的函数。当它给了一个长度是127的参数,它期望得到一个长度是127的答案。如果它没有得到它想要的,它就会表示它的不满:
> fun1 <- function(x) sin(x) + sin(x-1) + sin(x-2) + sin(x-3)
> integrate(fun1, 0, 2)
-1.530295 with absolute error < 2.2e-14
> fun2 <- function(x) sum(sin(x - 0:3))
> integrate(fun2, 0, 2)
Error in integrate(fun2, 0, 2) :
evaluation of function gave a result of wrong length
In addition: Warning message:
longer object length is not a multiple of shorter object length in: x - 0:3
> fun3 <- function(x) rowSums(sin(outer(x, 0:3, ’-’)))
> integrate(fun3, 0, 2)
-1.530295 with absolute error < 2.2e-14
fun1
是我们想要的的直接了当的实现,但是很难去概括。fun2
是模拟fun1
的拙略的尝试。fun3
是一个使用了outer
作为一步使向量化得到正确的答案的适当的实现。
8.3.12 outer
的非向量化函数
传递给outer
的函数需要向量化(在通常情况下):
> outer(1:3, 4:1, max)
Error in dim(robj) <- c(dX, dY) :
dims [product 12] do not match the length of object [1]
> outer(1:3, 4:1, pmax)
[,1] [,2] [,3] [,4]
[1,] 4 3 2 1
[2,] 4 3 2 2
[3,] 4 3 3 3
> outer(1:3, 4:1, Vectorize(function(x, y) max(x, y)))
[,1] [,2] [,3] [,4]
[1,] 4 3 2 1
[2,] 4 3 2 2
[3,] 4 3 3 3
函数可以用来转换一个函数(本质上是加加一个循环—向量化函数并没有什么神奇)。
8.3.13 忽视错误
你有一个循环其中一些迭代可能产生一些错误。你想忽略这些错误并让循环继续执行。一个解决方案是使用try
:
代码:
ans <- vector(’list’, n)
for(i in seq(length.out=n))
{
ans[[i]] <- rpois(round(rnorm(1, 5, 10)), 10)
}
当所要求的泊松变量的数值为负的情况下会失败。可以这样修改:
ans <- vector(’list’, n)
for(i in seq(length.out=n))
{
this.ans <- try(rpois(round(rnorm(1, 5, 10)), 10))
if(!inherits(this.ans, ’try-error’))
{
ans[[i]] <- this.ans
}
}
另外一个方法是使用tryCatch
而不是try
:
ans <- vector(’list’, n)
for(i in seq(length.out=n))
{
ans[[i]] <- tryCatch(rpois(round(rnorm(1, 5, 10)), 10),
error=function(e) NaN)
}
8.3.14 意外的全局
让函数在被创造的地方工作是可能的,但是不要在一般下工作。函数中的对象会意外地变为全局的。
> myfun4 <- function(x) x + y
> myfun4(30)
[1] 132
> rm(y)
> myfun4(30)
Error in myfun4(30) : Object "y" not found
函数findGlobals
可以高亮全局变量:
> library(codetools)
> findGlobals(myfun4)
[1] "+" "y"
8.3.15 掌控"..."
"..."
构造直到你直到这个特技之前你都可以持有这个光滑的东西。一种方法是将其包进一个列表:
function(x, ...)
{
dots <- list(...)
if(’special.arg’ %in% names(dots))
{
# rest of function
}
}
另一种方法是使用match.call
function(x, ...)
{
extras <- match.call(expand.dots=FALSE)$...
# rest of function
}
如果你的函数要处理一个参数,那么你或许需要使用do.call
:
function(x, ...)
{
# ...
dots <- list(...)
ans <- do.call(’my.other.fun’, c(list(x=x), dots[names(dots) %in% spec]))
# ...
}
8.3.16 懒惰
R采用懒惰的评估。意思是,函数的参数直到它们需要的时候才会被评估。如果该参数被证明是不需要的,这可以节省时间和内存。
在极度稀有的情况下,一个参数本该评估却没有被评估。
你可以使用force
强制克服懒惰。
> xr <- lapply(11:14, function(i) function() i^2)
> sapply(1:4, function(j) xr[[j]]())
[1] 196 196 196 196
> xf <- lapply(11:14, function(i) {force(i); function() i^2})
> sapply(1:4, function(j) xf[[j]]())
[1] 121 144 169 196
额外的信用来理解在xr
中发生的事情。
8.3.17 懒惰的lapply
lapply
不会评估它的FUN
参数。大多数情况下你不需要关心。如果该函数是一个泛型函数的话就会更加有效。这样做是更安全的:
lapply(xlist, function(x) summary(x))
比起:
lapply(xlist, summary)
8.3.18 不可见的斗篷
在很少的情况下,结果的可视性或许不是所期望的那样:
> myfun6 <- function(x) x
> myfun6(zz <- 7)
> .Last.value
[1] 7
> a6 <- myfun6(zz <- 9)
> a6
[1] 9
> myfun6(invisible(11))
> myfun7 <- function(x) 1 * x
> myfun7(invisible(11))
[1] 11
8.3.19 默认参数的评估
考虑:
> myfun2 <- function(x, y=x) x + y
> x <- 100
> myfun2(2)
[1] 4
> myfun2(2, x)
[1] 102
一些人会期待上述的两个函数调用的结果是相同的。它们不是。一个函数的一个参数的默认值在函数里边评估,不是在调用函数的环境中。
因此写一个类似下边的函数将不会得到你想要的。
> myfun3 <- function(x=x, y) x + y
> myfun3(y=3)
Error in myfun3(y = 3) : recursive default argument reference
(在你的R的版本中你得到的实际错误信息可能不一样。)
在这个方面最流行的错误是模仿参数的默认值。像这样:
> myfun5 <- function(x, n=xlen) { xlen <- length(x); ...}
> myfun5(myx, n=xlen-2)
xlen
在myfun5
里边定义,当调用myfun5
的时候是不可以使用xlen
的。
8.3.20 sapply
简单化
函数sapply
“简化”了lapply
的输出。并不总是如此简单。意思是,你得到的简单化或许不是你所期望的简单化。不确定性使sapply
在函数中使用不是很合适。有时vapply
是一个更安全的替代。