【问题标题】:R: Evaluating a script in an environmentR:在环境中评估脚本
【发布时间】:2017-05-05 03:34:33
【问题描述】:

我想在指定环境中评估的脚本中加载库函数。

例子:

## foo.R
## -----

## blah blah 

library(extrafont)
loadfonts()

为方便起见,假设评估环境是基础环境:

sys.source("foo.R")
## Registering fonts with R
## Error in eval(expr, envir, enclos) : could not find function "loadfonts"

extrafont:::loadfonts() 替换loadfonts() 效果更好,但仍然给出:

Error in get(as.character(FUN), mode = "function", envir = envir) : 
  object 'pdfFonts' of mode 'function' was not found

因为loadfonts() 需要在grDevices 中定义的pdfFonts()

【问题讨论】:

  • e<- new.env() source("foo.R", local=e) 将仅在该环境中评估您的 foo.R。所以你也可以在 foo.R 中需要 grDevices 库
  • @waterling:添加了答案

标签: r scope eval r-environment


【解决方案1】:

这既不是完全令人满意的答案,也是对@waterling 的长评论。

建议的解决方案是:

e<- new.env() 
source("foo.R", local=e)

source("foo.R", local=new.env())

基本上等同于:

sys.source("foo.R", envir=new.env())

它的工作原理大致相同:

sys.source("foo.R", envir=as.environment("package:grDevices"))

如报错(见问题),函数未找到,pdfFonts()是包grDevices的一部分上面的sys.sourcepackage:grDevices环境中执行脚本,因此找到了函数。相反,默认情况下 sys.source(..., envir=baseenv()) 和基本环境位于 grDevices 之前,因此找不到 pdfFonts()

第一个问题是我事先不知道哪些函数会出现在我的脚本中。 在这种情况下,设置envir=new.env() 是一种更通用的方法。默认 new.env(parent=parent.frame()), 因此它与sys.source() 具有相同的父级,即全局环境。因此,全局环境中可见的所有内容都在带有sys.source(..., envir=new.env()) 的脚本中可见,即用户创建的每个对象以及用户加载的包。

这里的问题是我们不再隔离脚本,这使得它的可重复性和稳定性降低。实际上,这取决于我们调用sys.source 的那一刻R 内存中的内容。 为了使事情更实用,这意味着foo.R 可能会起作用,因为我们通常bar.R 之后调用它。

第二个问题是这不是一个实际的解决方案。 问题涉及如何在e 环境中运行脚本foo.R 并在需要时仍访问不属于e 的功能。 采用e (直接或通过它的父母)可以访问这些功能实际上是一种解决方法,而不是解决方案。

如果这种解决方法是唯一可行的方法,恕我直言,最好的办法是使其仅依赖于标准 R 包。
一开始,R 显示:

search()
## [1] ".GlobalEnv"        "package:stats"     "package:graphics" 
## [4] "package:grDevices" "package:utils"     "package:datasets" 
## [7] "package:methods"   "Autoloads"         "package:base"     

那是八个官方包/环境。
除非明确更改默认值,否则新的包/环境会进入第二个插槽,而第一个插槽之后的所有包/环境都会移动一个位置。

myEnv=new.env()
attach(myEnv)
search()
##  [1] ".GlobalEnv"        "myEnv"             "package:stats"    
##  [4] "package:graphics"  "package:grDevices" "package:utils"    
##  [7] "package:datasets"  "package:methods"   "Autoloads"        
## [10] "package:base"     

所以我们可以在搜索路径中取最后八个,这意味着取这八个中的第一个继承其他的。我们需要:

pos.to.env(length(search()) - 7)
## <environment: package:stats>
## attr(,"name")
## [1] "package:stats"
## attr(,"path")
## [1] "path/to//R/R-x.x.x/library/stats"

因此:

sys.source("foo.R", envir=new.env(parent=pos.to.env(length(search()) - 7)))

或者可以采用标准的 R 参考包,例如 stats,以及它的父级。

因此:

sys.source("foo.R", envir=new.env(parent=as.environment("package:stats")))

更新

我找到了

解决方案

至于脚本:

#foo.R
#-----
library(extrafont)
f=function() loadfonts()
environment(f) = as.environment("package:extrafont")
f()

在新环境中执行:

sys.source("foo.R", envir=new.env(parent=baseenv()))

f() 现在可以访问包extrafont 中的所有对象以及之前加载的对象。

sys.source() 中创建一个 new.env() 与任何必要的父级以使 environment() 分配工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 2020-11-16
    • 2010-10-14
    相关资源
    最近更新 更多