在受限环境中使用`rmarkdown :: render`

时间:2018-10-09 15:37:13

标签: r r-markdown knitr r-environment

我有以下名为Rmd的{​​{1}}文件:

test.Rmd

我想通过以下方式调用渲染:

---
title: "test"
output: html_document
---

```{r}
print(y)
```

```{r}
x <- "don't you ignore me!"
print(x)
```

但失败:

render('test.Rmd', output_format = "html_document",
        output_file = 'test.html',
        envir = list(y="hello"))

第一个块运行得很好,所以工作正常。如果我在全局环境中定义processing file: test.Rmd |................ | 25% ordinary text without R code |................................ | 50% label: unnamed-chunk-1 |................................................. | 75% ordinary text without R code |.................................................................| 100% label: unnamed-chunk-2 Quitting from lines 11-13 (test.Rmd) Error in print(x) : object 'x' not found ,则可以在不使用y参数的情况下运行它,并且效果很好。

我发现envir可能不喜欢列表,所以我们给它一个适当的环境:

render

但是更糟糕的是,找不到y_env <- as.environment(list(y="hello")) ls(envir = y_env) # [1] "y" render('test.Rmd', output_format = "html_document", output_file = 'test.html', envir = y_env)

print

现在文档中提到使用功能processing file: test.Rmd |................ | 25% ordinary text without R code |................................ | 50% label: unnamed-chunk-1 Quitting from lines 7-8 (test.Rmd) Error in eval(expr, envir, enclos) : could not find function "print" ,所以出于绝望,我尝试这样做:

new.env

现在可以使用了!

y_env <- new.env()
y_env$y <- "hello"
render('test.Rmd', output_format = "html_document",
       output_file = 'test.html',
       envir = y_env)

回顾一下,我对几件事感到困惑:

  • 为什么processing file: test.Rmd |................ | 25% ordinary text without R code |................................ | 50% label: unnamed-chunk-1 |................................................. | 75% ordinary text without R code |.................................................................| 100% label: unnamed-chunk-2 output file: test.knit.md "C:/Program Files/RStudio/bin/pandoc/pandoc" +RTS -K512m -RTS test.utf8.md --to html --from markdown+autolink_bare_uris+ascii_identifiers+tex_math_single_backslash --output test.html --smart --email-obfuscation none --self-contained --standalone --section-divs --template "**redacted**\RMARKD~1\rmd\h\DEFAUL~1.HTM" --no-highlight --variable highlightjs=1 --variable "theme:bootstrap" --include-in-header "**redacted**\AppData\Local\Temp\RtmpGm9aXz\rmarkdown-str3f6c5101cb3.html" --mathjax --variable "mathjax-url:https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" Output created: test.html 会识别列表(第一个块未失败),却忽略了块中的常规分配
  • 为什么我的第二次尝试不起作用,它与我的第三次尝试有什么不同?
  • 这是一个错误吗?
  • 惯用的方式是什么?

1 个答案:

答案 0 :(得分:9)

您的前两个示例由于不同的原因而失败。要了解这两种失败,首先必须了解有关 knitr rmarkdown 如何评估代码块的知识。


编织器的通用代码块评估程序

当您在文件上调用rmarkdown::render()时,每个代码块最终都会通过调用evaluate::evaluate()进行评估。就其评估行为和作用域规则而言,evaluate()的行为几乎与基本R函数eval()相似。

evaluate::evaluate()eval()的最大不同之处在于它如何处理每个求值表达式的输出。如?evaluate中所述,除了求值作为第一个参数传递的表达式之外, ,它“捕获了重新创建输出所必需的所有信息,就好像您已将代码复制并粘贴到R终端中一样。”该信息包括图形以及警告和错误消息,这就是为什么在针织!)

无论如何,最终从函数evaluate()中对knitr:::block_exec()的调用看起来像这样

evaluate::evaluate(code, envir = env, ...)

其中:

  • code是字符串的向量,给出构成当前块的(可能是多个)表达式。

  • env是您在对envir的原始调用中提供了rmarkdown::render()形式参数的值。


您的第一个示例

在您的第一个示例中,envir是一个列表,而不是环境。在这种情况下,评估是在函数调用创建的本地环境中进行的。首先在通过?eval的列表中查找未解析的符号(在?evaluateenvir中都有记录),然后在{{1}给定的环境链中查找}参数。至关重要的是,分配是在临时评估环境中进行的,临时评估环境在函数调用完成后就不复存在了。

因为enclos一次对表达式的字符向量进行操作,所以当evaluate()是一个列表时,在其中一个表达式中创建的变量将无法在后续表达式中使用表达式。

envir的{​​{1}}参数是一个列表时,您的代码块最终将通过如下调用进行评估:

envir

效果与您用rmarkdown::render()进行的效果完全相同:

library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
          'print(x)')
env <- list(y = 1:10)
evaluate(code, envir = env)

## Or, for prettier printing:
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## > print(x)
## Error in print(x): object 'x' not found

您的第二个示例

eval()env <- list(y =1 :10) eval(quote(x <- "don't you ignore me"), envir = env) eval(quote(x), envir = env) ## Error in eval(quote(x), envir = env) : object 'x' not found 返回的环境时,由于其他原因会出现错误。在这种情况下,您的代码块最终将通过如下调用进行评估:

envir=

您已经注意到,此操作失败是因为as.environment(list())返回了一个环境,该环境的周围环境为空环境(即library(evaluate) code <- c('x <- "don\'t you ignore me!"', 'print(x)') env <- as.environment(list(y = 1:10)) evaluate(code, envir = env) ## Or, for prettier printing: replay(evaluate(code, envir = env)) ## > x <- "don't you ignore me!" ## Error in x <- "don't you ignore me!": could not find function "<-" ## > print(x) ## Error in print(x): could not find function "print" 返回的环境)。 as.environment()(就像emptyenv()一样)会在evaluate()中寻找符号eval(),当找不到符号<-时,便启动了一系列封闭环境,此处,不包含任何匹配项。 (还请注意,当env是环境而不是列表时,不使用envir参数。)


推荐的解决方案

要执行所需的操作,您需要创建一个环境,该环境:(1)包含列表中的所有对象以及该对象; (2)包含您对enclos的调用的父环境(即通常评估对render()的调用的环境)作为其封闭环境。最简洁的方法是使用漂亮的render()函数,如下所示:

list2env()

这样做将导致您的代码块被如下所示的代码评估:

env <- list2env(list(y="hello"), parent.frame())
render('test.Rmd', output_format = "html_document",
        output_file = 'test.html',
        envir = env)