可以lapply不修改更高范围内的变量

ste*_*ejb 15 r function lapply

我经常想做以下几点:

mat <- matrix(0,nrow=10,ncol=1)
lapply(1:10, function(i) { mat[i,] <- rnorm(1,mean=i)})
Run Code Online (Sandbox Code Playgroud)

但是,我希望mat里面会有10个随机数,但它有0个.(我并不担心rnorm部分.显然有一种正确的方法可以做到这一点.我担心会影响mat lapply的匿名函数)我可以不从lapply里面影响矩阵垫吗?为什么不?是否有R的范围规则阻止了这个?

Sha*_*ane 28

我在这个相关的问题中讨论了这个问题:" R是否适用于家庭而不是语法糖 ".您会注意到,如果您查看函数签名for forapply,它们有一个关键区别:for循环计算表达式,而apply循环计算函数.

如果你想改变apply函数范围之外的东西,那么你需要使用<<-assign.或者更重要的是,使用类似for循环的东西.但是,在处理函数之外的事情时,您确实需要小心,因为它可能导致意外行为.

在我看来,使用apply函数的一个主要原因是明确的,因为它不会改变它之外的东西.这是函数式编程的核心概念,其中函数避免了副作用.这也是为什么apply函数族可以用于并行处理(并且类似的函数存在于诸如雪的各种并行包中)的原因.

最后,运行代码示例的正确方法是将参数传递给函数,并分配输出:

mat <- matrix(0,nrow=10,ncol=1)
mat <- matrix(lapply(1:10, function(i, mat) { mat[i,] <- rnorm(1,mean=i)}, mat=mat))
Run Code Online (Sandbox Code Playgroud)

在可能的情况下(因此mat=mat)最好明确参数,而不是推断它.


hat*_*rix 6

高阶函数的主要优点之一是,lapply()或者sapply()您不必初始化"容器"(在这种情况下为矩阵).

正如Fojtasek建议的那样:

as.matrix(lapply(1:10,function(i) rnorm(1,mean=i)))
Run Code Online (Sandbox Code Playgroud)

或者:

do.call(rbind,lapply(1:10,function(i) rnorm(1,mean=i)))
Run Code Online (Sandbox Code Playgroud)

或者,简单地作为数字向量:

sapply(1:10,function(i) rnorm(1,mean=i))
Run Code Online (Sandbox Code Playgroud)

如果您真的想要修改匿名函数范围之上的变量(在此实例中为随机数生成器),请使用 <<-

> 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
Run Code Online (Sandbox Code Playgroud)

这个职位有关<<-.但是在这个特定的例子中,for循环会更有意义:

mat <- matrix(0,nrow=10,ncol=1)
for( i in 1:10 ) mat[i,] <- rnorm(1,mean=i)
Run Code Online (Sandbox Code Playgroud)

i在全局工作空间中创建索引变量的次要成本.