词法范围和R中的环境

问题描述 投票:0回答:1

我有以下代码片段,灵感来自我的原始代码。

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_obj 是不能在 func2(). 即使是更高的全局环境变量也是可以解析的,如 text1_obj.
  2. 为什么在debug模式下显式命名环境变量的效果很好,如 exists('text3_obj', where = parent.frame())eval(parse(text = 'text3_obj'), parent.frame())
r lexical-scope
1个回答
3
投票

你混淆了 "调用环境 "和 "包围环境"。 查看Hadley的 "Advanced R "一书中的这些术语。

http:/adv-r.had.co.nzEnvironments.html

调用环境 "是指函数被调用的环境,并由一个名字很不幸的函数 parent.frame. 然而,调用环境并不用于词法作用域。

包围环境 "是创建函数的环境,用于词法作用域。 您已经创建了两个 func1func2 的全局环境中。 因此,全局环境是这两个函数的 "包围环境",并将用于词法范围的确定。无论调用环境如何!

如果你想 func2 的执行环境来使用 func1 对于词法范围,您有(至少)两种选择。 您可以创建 func2func1

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))
}

这也能达到预期的效果。

在编写演示代码时,我发现了一个有趣的小插曲......。 似乎当你重新分配环境的 func2 从内部 func1的副本,R创建了一个 func2 在执行环境中 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!!"
© www.soinside.com 2019 - 2024. All rights reserved.