lapply函数是否不能修改较高作用域中的变量?

17

我经常想做的基本上是以下:

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中是否有作用域规则阻止了这一点?

3个回答

35

我在这个相关的问题中讨论了这个问题:“Is R’s apply family more than syntactic sugar”。 如果你查看 forapply 的函数签名,你会注意到它们有一个重要的区别: 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),而不是推测它。


8

高阶函数(例如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,可以以较小的代价实现。


1

而不是实际改变 mat,lapply 只是返回修改后的 mat 版本(作为一个列表)。你只需要将其赋值给 mat,并使用 as.matrix() 将其转换回矩阵。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接