我经常想做的基本上是以下:
mat <- matrix(0,nrow=10,ncol=1)
lapply(1:10, function(i) { mat[i,] <- rnorm(1,mean=i)})
但是,我期望mat中会有10个随机数,但实际上却是0。(我不担心rnorm部分。显然有正确的方法来做到这一点。我担心在lapply的匿名函数内部影响mat)我不能从lapply内部影响矩阵mat吗?为什么?R中是否有作用域规则阻止了这一点?
我在这个相关的问题中讨论了这个问题:“Is R’s apply family more than syntactic sugar”。
如果你查看 for
和 apply
的函数签名,你会注意到它们有一个重要的区别: for
循环评估一个表达式,而 apply
循环评估一个函数。
如果您想在 apply 函数的作用域之外更改东西,则需要使用 <<-
或 assign
。或者更准确地说,可以使用类似 for
循环的东西。但是在处理函数之外的事物时,一定要非常小心,因为它可能导致意外行为。
在我看来,使用 apply
函数的主要原因之一是明确因为它不会改变其作用域之外的东西。这是函数式编程的核心概念,其中函数避免具有 副作用。这也是为什么 apply
函数族可以用于并行处理的原因(各种并行包中都存在类似的函数,例如 snow)。
最后,运行您的代码示例的正确方法是将参数传递到函数中,并将输出赋回:
mat <- matrix(0,nrow=10,ncol=1)
mat <- matrix(lapply(1:10, function(i, mat) { mat[i,] <- rnorm(1,mean=i)}, mat=mat))
尽可能明确一个参数总是最好的做法(因此使用mat = mat
),而不是推测它。
高阶函数(例如lapply()
或sapply()
)的主要优点之一是您不必初始化您的“容器”(在本例中为矩阵)。
正如Fojtasek所建议的:
as.matrix(lapply(1:10,function(i) rnorm(1,mean=i)))
或者:
do.call(rbind,lapply(1:10,function(i) rnorm(1,mean=i)))
sapply(1:10,function(i) rnorm(1,mean=i))
如果您真的想修改匿名函数作用域之外的变量(在此例中为随机数生成器),请使用 <<-
。
> mat <- matrix(0,nrow=10,ncol=1)
> invisible(lapply(1:10, function(i) { mat[i,] <<- rnorm(1,mean=i)}))
> mat
[,1]
[1,] 1.6780866
[2,] 0.8591515
[3,] 2.2693493
[4,] 2.6093988
[5,] 6.6216346
[6,] 5.3469690
[7,] 7.3558518
[8,] 8.3354715
[9,] 9.5993111
[10,] 7.7545249
参见这篇文章关于<<-
的使用。但在这个特定的例子中,使用for循环会更加合理:
mat <- matrix(0,nrow=10,ncol=1)
for( i in 1:10 ) mat[i,] <- rnorm(1,mean=i)
通过在全局工作区创建一个索引变量i
,可以以较小的代价实现。
而不是实际改变 mat,lapply 只是返回修改后的 mat 版本(作为一个列表)。你只需要将其赋值给 mat,并使用 as.matrix()
将其转换回矩阵。