R:在环境中评估脚本
R: Evaluating a script in an environment
我想在指定环境中评估的脚本中加载库函数。
示例:
## 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()
。
这既是一个不完全令人满意的答案,也是对@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
执行 [=24] 中的脚本=] 环境,因此找到了函数。相反,默认情况下 sys.source(..., envir=baseenv())
并且基础环境出现在 grDevices
之前,因此找不到 pdfFonts()
。
第一个问题是我事先不知道我的脚本中会出现哪些函数。
在这种情况下,设置 envir=new.env()
是一种更通用的方法。默认情况下
new.env(parent=parent.frame())
,
因此它具有与sys.source()
相同的parent,即全局环境。因此,全局环境中可见的所有内容在带有 sys.source(..., envir=new.env())
的脚本中都是可见的,即用户创建的每个 object 和用户加载的包。
这里的问题是我们不再隔离脚本,这使得它的可重现性和稳定性降低。事实上,这取决于我们调用 sys.source
的那一刻 R 内存中的内容。
为了使事情更实用,这意味着 foo.R
可能只是因为我们 通常 在 bar.R
之后调用它。
第二个问题是这不是实际的解决方案。
问题涉及 如何在环境 e
中 运行 脚本 foo.R
并且在需要时仍然访问不属于 e
的函数。 让一个(直接或通过它的 parents)访问这些功能的 e
实际上是一种变通方法,而不是解决方案。
如果这种解决方法是唯一可行的方法,恕我直言,最好的办法是让它只依赖于标准 R 包。
开始时,R 显示:
search()
## [1] ".GlobalEnv" "package:stats" "package:graphics"
## [4] "package:grDevices" "package:utils" "package:datasets"
## [7] "package:methods" "Autoloads" "package:base"
即八官packages/environments.
新 packages/environments,除非明确更改默认值,否则进入第二个槽位,第一个槽位之后的所有槽位移动一个位置。
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
,及其 parents。
因此:
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
中的所有 object 以及之前加载的那些。
在 sys.source()
中创建一个 new.env()
和任何 parent 是使 environment()
分配工作所必需的。
我想在指定环境中评估的脚本中加载库函数。
示例:
## 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()
。
这既是一个不完全令人满意的答案,也是对@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
执行 [=24] 中的脚本=] 环境,因此找到了函数。相反,默认情况下 sys.source(..., envir=baseenv())
并且基础环境出现在 grDevices
之前,因此找不到 pdfFonts()
。
第一个问题是我事先不知道我的脚本中会出现哪些函数。
在这种情况下,设置 envir=new.env()
是一种更通用的方法。默认情况下
new.env(parent=parent.frame())
,
因此它具有与sys.source()
相同的parent,即全局环境。因此,全局环境中可见的所有内容在带有 sys.source(..., envir=new.env())
的脚本中都是可见的,即用户创建的每个 object 和用户加载的包。
这里的问题是我们不再隔离脚本,这使得它的可重现性和稳定性降低。事实上,这取决于我们调用 sys.source
的那一刻 R 内存中的内容。
为了使事情更实用,这意味着 foo.R
可能只是因为我们 通常 在 bar.R
之后调用它。
第二个问题是这不是实际的解决方案。
问题涉及 如何在环境 e
中 运行 脚本 foo.R
并且在需要时仍然访问不属于 e
的函数。 让一个(直接或通过它的 parents)访问这些功能的 e
实际上是一种变通方法,而不是解决方案。
如果这种解决方法是唯一可行的方法,恕我直言,最好的办法是让它只依赖于标准 R 包。
开始时,R 显示:
search()
## [1] ".GlobalEnv" "package:stats" "package:graphics"
## [4] "package:grDevices" "package:utils" "package:datasets"
## [7] "package:methods" "Autoloads" "package:base"
即八官packages/environments.
新 packages/environments,除非明确更改默认值,否则进入第二个槽位,第一个槽位之后的所有槽位移动一个位置。
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
,及其 parents。
因此:
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
中的所有 object 以及之前加载的那些。
在 sys.source()
中创建一个 new.env()
和任何 parent 是使 environment()
分配工作所必需的。