knitr从用户的环境继承变量,即使使用envir = new.env()

Dav*_*son 13 r knitr

我发现knitr文档继承了用户环境中的变量,即使envir = new.env()提供了参数.如何防止它继承这些变量?

例如,假设我使用不存在的变量(y)编写了一个简单的.Rmd文件,编织它并显示生成的文件:

library(knitr)
writeLines(c("```{r}", "y + 1", "```"), "test.Rmd")
knit("test.Rmd", quiet = TRUE, envir = new.env())
# [1] "test.md"
cat(readLines("test.md"), sep = "\n")
# 
# ```r
# y + 1
# #> Error in eval(expr, envir, enclos): object 'y' not found
# ```
Run Code Online (Sandbox Code Playgroud)

当然,我得到一个错误,即y变量不存在,就像我应该的那样.

但是,如果我y在我自己的环境中定义,我发现我现在可以y在.Rmd文件中引用,即使我给出了envir = new.env()参数.

y <- 3
knit("test.Rmd", quiet = TRUE, envir = new.env())
# [1] "test.md"
cat(readLines("test.md"), sep = "\n")
#
# ```r
# y + 1
# # [1] 4
# ```
Run Code Online (Sandbox Code Playgroud)

我的理解是,envir = new.env()应该导致knitr文档在没有y变量的新环境中进行评估.这是一个问题,因为它允许knitr文档不可再现,指的是我没有在文档中定义的变量.

请注意,rmarkdown render 文档(它是一个包装器knit)明确说明您可以使用envir = new.env():

在编织期间评估代码块的环境(可以使用new.env()来保证空的新环境).

但是,render出于同样的原因,显示出与上述相同的行为.我的期望(和rmarkdown文档)是不正确的envir = new.env(),还是我错误地使用它?还有另一种方法可以保证文档中的新环境被编织?

Kon*_*lph 9

new.env有一个parent参数,其默认值是parent.frame()- 即调用者.换句话说,您的新环境继承了当前环境中的所有内容.

您可以通过指定parent:

new.env(parent = baseenv())
Run Code Online (Sandbox Code Playgroud)

或者,如果要继承已加载的包:

new.env(parent = as.environment(2))
Run Code Online (Sandbox Code Playgroud)

而且,是的,render文档有点误导:虽然new.env()提供了一个新的,空的环境,但它并没有完全与调用者分离,调用者可能几乎从不想使用它new.env().

为了能够在继承的干净环境中使用包baseenv(),您需要手动实现包附件机制,因为R包本身不支持环境隔离(grrr!).或者您使用"modules"包,它支持本地连接的包:

```{r}
modules::import_package('ggplot2', attach = TRUE)
qplot(rnorm(10))
```
Run Code Online (Sandbox Code Playgroud)

attach = TRUE参数导致包本地附加,不像library.

这是一个可以使用的"模块"包加载代码的精简版本:

require_namespace = function (package) {
    ns = .Internal(getRegisteredNamespace(package))
    if (is.null(ns))
        ns = tryCatch(loadNamespace(package), error = identity)

    ns
}

exhibit_package_namespace = function (namespace, name, parent, export_list) {
    structure(list2env(sapply(export_list, getExportedValue, ns = namespace,
                              simplify = FALSE),
                       parent = parent.env(parent)),
              name = paste('package', name, sep = ':'),
              path = getNamespaceInfo(namespace, 'path'))
}

library_local = function (package, parent = parent.frame()) {
    pkg_ns = require_namespace(package)
    if (inherits(pkg_ns, 'error'))
        stop('Unable to load package ', sQuote(package), '\n',
             'Failed with error: ', sQuote(conditionMessage(pkg_ns)))

    export_list = getNamespaceExports(pkg_ns)
    pkg_env = exhibit_package_namespace(pkg_ns, package, parent, export_list)
    parent.env(parent) = pkg_env
}
Run Code Online (Sandbox Code Playgroud)

用法:

```{r}
library_local('ggplot2')
qplot(rnorm(10))
```
Run Code Online (Sandbox Code Playgroud)