R 中的词法作用域和环境

lexical scoping and environments in R

我有以下受我原始代码启发的代码片段:

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_objtext2_obj)并将它们的名称传递给 func1()。此函数在 vector 中添加另一个对象后,调用 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. 为什么在调试模式下显式命名环境像 charm 一样有效,例如exists('text3_obj', where = parent.frame())eval(parse(text = 'text3_obj'), parent.frame())

您将 "calling environment" 与 "enclosing environment." 混淆了,请查看 Hadley 的书中的这些术语 "Advanced R."

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

"calling environment" 是调用函数的环境,由不幸命名的函数 parent.frame 返回。但是,调用环境不用于词法范围。

"enclosing environment" 是创建函数的环境,用于词法范围。您已经在全局环境中创建了 func1func2。因此,全局环境是两个函数的 "enclosing environment" 并且将用于词法范围 而不管调用环境如何!!

如果你想让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.

中重新分配它的 "enclosing environment"
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 似乎创建了 [=20] 的副本=]在func1的执行环境中。当您返回控制台时,原始 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() 时返回。

事实证明,您可以使用 <<-:

在全局环境中强制更改为原始 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!!"