【问题标题】:lapply() emptied list step by step while processinglapply() 在处理时逐步清空列表
【发布时间】:2015-04-26 21:15:21
【问题描述】:

首先,请原谅我的标题不好。我仍然对这种行为感到困惑,以至于我无法描述它;但是我能够重现它并将其分解为一个(愚蠢的)示例。

请你好心解释一下为什么在调用lapply()之后other.list似乎充满了NULLs?

some.list <- rep(list(rnorm(1)),33)
other.list <- rep(list(), length = 33)

lapply(seq_along(some.list), function(i, other.list) {
  other.list[[i]] <- some.list[[i]]
  browser()
}, other.list)

我在 RStudio 的调试模式下观看了这个。对于某些iother.list[[i]] 被分配了some.list[[i]],但在下一次迭代中它将为空。我想了解这种行为太糟糕了!

【问题讨论】:

  • 因为......您没有将函数调用的结果分配给命名项“其他 .list”。 (并且函数内部的“other.list”也没有真正累积结果)。下面提供的超级任务不是经验丰富的 R 用户处理数据对象的常用方法。通常的范例(假设您对函数的行为非常有信心)是other.list &lt;- lapply(seq_along(some.list), &lt;function-call&gt; , some.list)。这是一种有点啰嗦的说法:other.list &lt;- lapply(some.list, &lt;function-call_with_no_seq_param&gt; )

标签: r null scope environment-variables lapply


【解决方案1】:

原因是赋值发生在函数内部,而您使用的是普通赋值运算符&lt;-,而不是超赋值运算符&lt;&lt;-。在函数范围内时,IOW 执行函数时,正常赋值运算符始终分配给为该函数的特定评估创建的评估环境中的局部变量(通过从函数内部调用 environment() 返回与fun=NULL)。因此,在全局环境中定义的全局 other.list 变量(由 globalenv() 返回)不会被这样的赋值影响。另一方面,超赋值运算符将沿着闭包环境链(可以通过parent.env() 递归地跟随)返回,直到它在赋值的 LHS 上找到一个具有名称的变量,然后对其进行赋值。全局环境始终位于闭包环境链的基础上。如果没有找到这样的变量,超赋值运算符会在全局环境中创建一个。

因此,如果您在函数内部发生的赋值中将&lt;- 更改为&lt;&lt;-,您将能够修改全局other.list 变量。

https://stat.ethz.ch/R-manual/R-devel/library/base/html/assignOps.html

在这里,我尝试制作一个小演示来演示这些概念。在我的所有作业中,我都在分配包含被分配变量的实际环境:

oldGlobal <- environment(); ## environment() is same as globalenv() in global scope
(function() {
    newLocal1 <- environment(); ## creates a new local variable in this function evaluation's evaluation environment
    print(newLocal1); ## <environment: 0x6014cbca8> (different for every evaluation)
    oldGlobal <<- parent.env(environment()); ## target search hits oldGlobal in closure environment; RHS is same as globalenv()
    newGlobal1 <<- globalenv(); ## target search fails; creates a new variable in the global environment
    (function() {
        newLocal2 <- environment(); ## creates a new local variable in this function evaluation's evaluation environment
        print(newLocal2); ## <environment: 0x6014d2160> (different for every evaluation)
        newLocal1 <<- parent.env(environment()); ## target search hits the existing newLocal1 in closure environment
        print(newLocal1); ## same value that was already in newLocal1
        oldGlobal <<- parent.env(parent.env(environment())); ## target search hits oldGlobal two closure environments up in the chain; RHS is same as globalenv()
        newGlobal2 <<- globalenv(); ## target search fails; creates a new variable in the global environment
    })();
})();
oldGlobal; ## <environment: R_GlobalEnv>
newGlobal1; ## <environment: R_GlobalEnv>
newGlobal2; ## <environment: R_GlobalEnv>

【讨论】:

  • 这是相当有趣的代码,与其说是环境报告,不如说是用于本地函数的使用。它表明你来自编程世界的另一个角落。 (不过,您应该放弃使用不必要的分号。它们的唯一作用是减慢您的代码速度。)通常建议不要使用超赋值。它在这里用作教学工具是合理的,但不推荐使用更一般的用途。
【解决方案2】:

我没有运行你的代码,但有两个观察结果:

  1. 我通常避免将browser() 作为函数内的最后一行,因为它会被视为返回值

  2. other.list 不会被您的 lapply 修改。您需要了解环境的基础知识,并且您在 lapply 中进行的任何绑定都不能在它之外进行。这是一个设计特性,重点是 lapply 不会有副作用——你应该只使用它的返回值。您可以使用&lt;&lt;- 运算符代替&lt;-,尽管我不建议这样做,或者您可以使用assign 函数代替。或者您可以按照lapply 的使用方式正确执行此操作:

    others.list

请注意,通常建议不要在 lapply 内进行更改其外部变量的赋值。 lapply 旨在对每个元素执行一个函数并返回一个列表,该列表应该是 lapply 用于的所有内容

【讨论】:

  • 我没有尝试复制列表。我的问题中的代码是对我最初所做的事情的简化。但我不知道 browser() 是一个返回语句。很高兴知道!
  • @RobertKirsten:browser() 不是“返回语句”,而是函数中最后一次求值的结果是任何函数的返回值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-01
相关资源
最近更新 更多