【问题标题】:Nested function environment selection嵌套函数环境选择
【发布时间】:2013-08-22 16:44:56
【问题描述】:

我正在编写一些用于执行重复任务的函数,但我正在尽量减少加载数据的次数。基本上,我有一个函数可以获取一些信息并绘制图表。然后我有第二个函数,它将循环并将多个图输出到.pdf。在这两个函数中,我都有以下代码行:

if(load.dat) load("myworkspace.RData")

其中load.dat 是一个逻辑,我需要的数据存储在myworkspace.RData 中。当我调用循环并输出多个图的包装函数时,我不想在每次调用内部函数时重新加载工作区。我以为我可以在包装函数中加载一次工作区,然后内部函数可以访问该数据,但我得到一个错误说明。

所以我的理解是当一个函数在其本地环境中找不到变量时(在函数被调用时创建),该函数将在父环境中查找该变量。

我假设内部函数调用的父环境将是外部函数调用。显然这不是真的:

func1 <- function(...){
  print(var1)
}

func2 <- function(...){
  var1 <- "hello"
  func1(...)
}

> func2()
Error in print(var1) : object 'var1' not found

在阅读了大量问题、语言手册和 this 非常有用的博文后,我想到了以下内容:

var1 <- "hello"
save(list="var1",file="test.RData")
rm(var1)

func3 <- function(...){
  attach("test.RData")
  func1(...)
  detach("file:test.RData")
}

> func3()
[1] "hello"

有没有更好的方法来做到这一点?为什么func1 不在func2 创建的本地环境中查找未定义的变量,而实际上是func2 调用func1

注意:我不知道如何命名这个问题。如果有人有更好的建议,我会更改并编辑此行。

【问题讨论】:

  • 词法作用域意味着函数将在其父环境中查找未定义的符号,这不一定是调用环境。也检查一下:github.com/hadley/devtools/wiki/Environments
  • @Ferdinand.kraft 感谢您的链接。我会在今天下午解决这个问题。
  • 如果你的数据是数据框的形式,你可以使用包data.table,并将你的表作为参数传递给func3内的func1。此软件包通过引用起作用,不会对您的数据进行不必要的复制。
  • 不太清楚为什么它看不到var1,但请注意print(parent.frame()$var1) 工作正常。
  • @dayne,故意让func1 看不到这些环境。当您在控制台中输入func1 &lt;- function... 时,您将创建一个具有环境属性的闭包类型对象,该属性等于R_GlobalEnv。这是 R 将查找在评估 func1 的主体时未解析的符号的地方。在执行func2func3 期间创建的评估 环境是无关 WRT 符号查找。一种解决方法是使用 parent.frame()$var1,正如 Richie 上面指出的那样,但它非常难看。

标签: r function environment-variables


【解决方案1】:

你可以使用闭包:

f2 <- function(...){
   f1 <- function(...){
     print(var1)
   }
   var1 <- "hello"
   f1(...)
 }
 f2()

【讨论】:

  • 对,但我需要能够将内部函数用作独立函数。我不想每次调用外部函数时都必须重新定义内部函数(更不用说重复一堆代码)。
  • 那么我认为最干净的设置:将所有数据放入列表(my_data)中,然后将其作为函数的参数。在函数内部,您可以使用 with(my_data, { } ) 以避免额外输入。
【解决方案2】:

为了说明词法作用域,请考虑以下内容:

首先让我们创建一个沙盒环境,只是为了避免常见的 R_GlobalEnv:

sandbox <-new.env()

现在我们在其中放入两个函数:f,它查找名为 x 的变量;和g,它定义了一个本地的x并调用f

sandbox$f <- function()
{
    value <- if(exists("x")) x else "not found."
    cat("This is function f looking for symbol x:", value, "\n")
}

sandbox$g <- function()
{
    x <- 123
    cat("This is function g. ")
    f()
}

技术性:在控制台中输入函数定义会导致封闭环境设置为R_GlobalEnv,因此我们手动强制fg的封闭环境匹配它们“所属”的环境:

environment(sandbox$f) <- sandbox
environment(sandbox$g) <- sandbox

致电gf找不到局部变量x=123

> sandbox$g()
This is function g. This is function f looking for symbol x: not found. 

现在我们在全局环境中创建一个x 并调用gf 函数会先在沙箱中查找x,然后在沙箱的父级中查找,恰好是 R_GlobalEnv:

> x <- 456
> sandbox$g()
This is function g. This is function f looking for symbol x: 456 

只是为了检查f 是否首先在其外壳中查找x,我们可以在其中放置x 并调用g

> sandbox$x <- 789
> sandbox$g()
This is function g. This is function f looking for symbol x: 789 

结论:R 中的符号查找遵循封闭环境链,不是在执行嵌套函数调用期间创建的评估框架。

编辑:只需添加指向this very interesting answer from Martin Morgan on the related subject of parent.frame() vs parent.env()的链接

【讨论】:

  • 这是我见过的最好的插图。太感谢了!我并没有真正理解环境和框架的区别。
猜你喜欢
  • 2011-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多