【问题标题】:lexical scoping and environments in RR中的词法作用域和环境
【发布时间】:2020-04-30 15:37:02
【问题描述】:

我有以下代码 sn-p 灵感来自我的原始代码:

func2 <- function(foos) {
  for (foo in foos)
    print(eval(parse(text = foo)))
  return(foos)
}

func1 <- function(vec) {
  text3_obj <- 'text3'
  vec <- c(vec, c('text3_obj'))
  return(func2(vec))
}

text1_obj <- 'text1'
text2_obj <- 'text2'
func1(c('text1_obj', 'text2_obj'))

在主代码中,我创建了 2 个对象 (text1_obj & text2_obj) 并将它们的名称传递给 func1()。在向量中添加另一个对象后,此函数调用func2()。在func2() 我只是打印对象的值。下面是这段代码的输出:

[1] "text1"
[1] "text2"
Error in eval(parse(text = foo)) : object 'text3_obj' not found

进入调试模式后,我意识到即使是 exists('text3_obj') 内部的 func2() 也会抛出错误。所以我有两个问题:

  1. 即使func1()func2() 的直接父环境;为什么text3_objfunc2() 中不可解析。甚至更高的全局环境变量是可解析的,例如text1_obj
  2. 为什么在调试模式下显式命名环境就像魅力一样,例如exists('text3_obj', where = parent.frame())eval(parse(text = 'text3_obj'), parent.frame())

【问题讨论】:

    标签: r lexical-scope


    【解决方案1】:

    您将“调用环境”与“封闭环境”混淆了。在 Hadley 的《Advanced R》一书中查看这些术语。

    http://adv-r.had.co.nz/Environments.html

    “调用环境”是调用函数的环境,由不幸命名的函数parent.frame 返回。但是,调用环境不用于词法作用域。

    “封闭环境”是创建函数并用于词法作用域的环境。您已经在全局环境中创建了func1func2。因此,全局环境是两个函数的“封闭环境”,并将用于词法范围无论调用环境如何!!

    如果您希望func2 使用func1 的执行环境进行词法作用域,您有(至少)两个选项。你可以在func1内创建func2

    func1 <- function(vec) {
    
      func2 <- function(foos) {
        for (foo in foos)
          print(eval(parse(text = foo)))
        return(foos)
      }
    
      text3_obj <- 'text3'
      vec <- c(vec, c('text3_obj'))
      return(func2(vec))
    }
    

    然后您的测试按预期工作:

    > text1_obj <- 'text1'
    > text2_obj <- 'text2'
    > func1(c('text1_obj', 'text2_obj'))
    [1] "text1"
    [1] "text2"
    [1] "text3"
    [1] "text1_obj" "text2_obj" "text3_obj"
    

    或者,您可以创建func2 并从func1 中重新分配它的“封闭环境”。

    func2 <- function(foos) {
      for (foo in foos)
        print(eval(parse(text = foo)))
      return(foos)
    }
    
    func1 <- function(vec) {
      text3_obj <- 'text3'
      vec <- c(vec, c('text3_obj'))
      environment(func2) <- environment()
      return(func2(vec))
    }
    

    这也将按预期工作。

    我在编写演示代码时发现的一个有趣的花絮...似乎当您从func1 中重新分配func2 的环境时,R 在@ 的执行环境中创建了func2 的副本987654337@。当您返回控制台时,原始func2 的封闭环境保持不变。证人:

    a = function() {
      print(identical(environment(a), globalenv()))
    }
    
    b = function(x) {
      environment(a) <- environment()
      a()
    }
    

    测试a()b()

    > a()
    [1] TRUE
    > b()
    [1] FALSE
    > a()
    [1] TRUE
    >
    

    这不是我所期望的,但似乎 R 的表现非常出色。如果不是这样,a() 的封闭环境将永久更改为b() 的执行环境,并且 FALSE 应该在第二次调用 a() 时返回。

    事实证明,您可以在全局环境中使用&lt;&lt;- 强制更改为原始a()

    a = function() {
      print(identical(environment(a), globalenv()))
    }
    
    b = function(x) {
      # set a variable in the execution environment of b() for use later...
      montePython = "I'm not dead yet!!"
      # change the enclosing environment of a() in the global environment
      # rather than making a local copy of a() in b()'s execution environment.
      environment(a) <<- environment()
      a()
    }
    

    测试a()b()

    > a()
    [1] TRUE
    > b()
    [1] FALSE
    > a()
    [1] FALSE
    >
    

    有趣的是,这意味着b() 的(通常是临时的)执行环境即使在b() 终止后仍然存在于内存中,因为a() 仍然引用该环境,所以它不能被垃圾回收。证人:

    > environment(a)$montePython
    [1] "I'm not dead yet!!"
    

    【讨论】:

      猜你喜欢
      • 2021-03-24
      • 1970-01-01
      • 2013-06-26
      • 1970-01-01
      • 2013-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多