这既不是完全令人满意的答案,也是对@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.source在package: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() 分配工作。