14

我有以下Rmd我调用的文件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

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

  • 为什么render识别列表(第一个块没有失败)但随后忽略块中的常规分配
  • 为什么我的第二次尝试不起作用,它与我的第三次尝试有何不同?
  • 这是一个错误吗?
  • 这样做的惯用方法是什么?
4

1 回答 1

11

您的前两个示例因不同原因而失败。要理解这两种失败,首先要了解knitrrmarkdown是如何评估代码块的。


knitr 的通用代码块评估程序

当您调用rmarkdown::render()文件时,每个代码块最终都会通过调用evaluate::evaluate(). 就其评估行为和范围规则而言,其evaluate()行为几乎与基本 R 函数完全相同eval()

(与它evaluate::evaluate()最大的不同之处在于eval()它如何处理每个评估表达式的输出。如 中所述?evaluate,除了评估作为其第一个参数传递的表达式之外,它“捕获重新创建输出所需的所有信息,就好像您已经复制了并将代码粘贴到 R 终端”。该信息包括绘图、警告和错误消息,这就是为什么它在knitr之类的包中如此方便的原因!)

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

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

其中:

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

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


你的第一个例子

在您的第一个示例中,envir是一个列表,而不是一个环境。在这种情况下,评估是在函数调用创建的本地环境中执行的。首先在传递 a 的列表中查找未解析的符号(如?eval和中所述) ,然后在以参数给出的环境链开头的环境链中查找。至关重要的是,分配对于临时评估环境来说是本地的,一旦函数调用完成,它就会不存在。?evaluateenvirenclos

因为evaluate()一次操作一个表达式的字符向量,当envir是一个列表时,在这些表达式之一中创建的变量将不可用于后续表达式。

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()(like would)在eval()其中查找符号,当它在那里找不到时,启动封闭环境链,这里不包含任何匹配项。(还记得 when是一个环境,而不是一个列表,这个参数没有被使用。)<-envenvirenclos


推荐解决方案

为了做你想做的事,你需要创建一个环境:(1)包含列表中的所有对象;(2) 将调用的父环境作为其封闭环境render()(即render()通常评估调用的环境)。最简洁的方法是使用 niftylist2env()函数,如下所示:

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

这样做会导致您的代码块被如下代码评估,这就是您想要的:

library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
          'print(x)')
env <- list2env(list(y = 1:10), envir = parent.frame())
evaluate(code, envir = env)
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## > print(x)
## [1] "don't you ignore me!"
于 2018-10-09T18:57:32.990 回答