2

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

例子:

## 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"

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

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

因为loadfonts()需要pdfFonts()grDevices.

4

1 回答 1

1

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

建议的解决方案是:

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

IE

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.source上面在环境中执行脚本package:grDevices,因此找到了函数。相反,默认情况下sys.source(..., envir=baseenv()),基础环境在 之前grDevices,因此pdfFonts() 找不到。

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

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

第二个问题是这不是一个实际的解决方案。问题涉及如何foo.R在环境中运行脚本,e并且在需要时仍然可以访问不属于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()

于 2016-12-20T16:01:47.397 回答