在受限环境中使用`rmarkdown :: render` [英] Use `rmarkdown::render` in a restricted environment

查看:120
本文介绍了在受限环境中使用`rmarkdown :: render`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下名为test.RmdRmd文件:

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

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

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

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

I want to call render the following way:

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参数的情况下运行它,并且效果很好.

The first chunk went just fine, so something has worked. If I define y in my global environment I can run it without the envir argument and it works fine.

我认为render可能不喜欢列表,所以让我们为它提供一个适当的环境:

I figured maybe render doesn't like lists, so let's give it a proper environment :

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

But it's even worse, it doesn't find 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,因此出于绝望,我尝试这样做:

Now the docs mentions using the function new.env so out of despair I try this :

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

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

So I'm confused about several things, to recap :

  • 为什么render识别列表(第一个块没有失败),但是却忽略了块中的常规分配
  • 为什么我的第二次尝试不起作用,它与我的第三次尝试有什么不同?
  • 这是一个错误吗?
  • 惯用的方式是什么?
  • Why does render recognize lists (first chunk didn't fail) but then ignores regular assignments in the chunks
  • Why doesn't my second try work and how is it different from my third try ?
  • Is this a bug ?
  • What's the idiomatic way to do this ?

推荐答案

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

Your two first examples fail for different reasons. To understand both failures, it's first important to know a bit about how code chunks are evaluated by knitr and rmarkdown.

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

When you call rmarkdown::render() on your file, each code chunk is ultimately evaluated by a call to evaluate::evaluate(). In terms of its evaluation behavior and scoping rules, evaluate() behaves almost exactly like the base R function eval().

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

(Where evaluate::evaluate() differs most from eval() is in how it handles the output of each evaluated expression. As explained in ?evaluate, in addition to evaluating the expression passed as its first argument, it "captures all of the information necessary to recreate the output as if you had copied and pasted the code into an R terminal". That info includes plots and warning and error messages, which is why it's so handy in a package like knitr!)

在任何情况下,最终从函数knitr:::block_exec()内调用evaluate()的过程看起来都是这样的

In any case, the eventual call to evaluate(), from within the function knitr:::block_exec(), looks something like this

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

其中:

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

  • code is a vector of character strings giving the (possibly multiple) expressions making up the current chunk.

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

env is value that you supplied the envir formal argument in your original call to rmarkdown::render().

在您的第一个示例中,envir是一个列表,而不是环境.在这种情况下,评估是在由函数调用创建的本地环境中进行的.首先在通过envir的列表中查找未解析的符号(在?eval?evaluate中都有说明),然后在环境链中以enclos参数给出的符号开头查找.至关重要的是,分配对于临时评估环境而言是本地的,一旦函数调用完成,该环境就不复存在了.

In your first example, envir is a list, not an environment. When that is the case, evaluation is carried out in a local environment created by the function call. Unresolved symbols (as documented in both ?eval and ?evaluate) are looked for first in the list passed a envirand then in the chain of environments beginning with that given by the enclos argument. Assignments, crucially, are local to the temporary evaluation environment, which goes out of existence once the function call is complete.

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

Because evaluate() operates, one at a time, on a character vector of expressions, when envir is a list, variables created in one of those expression won't be available for use in the subsequent expressions.

rmarkdown::render()envir参数是列表时,您的代码块最终将通过如下调用进行评估:

When the envir argument to rmarkdown::render() is a list, your code block ultimately gets evaluated by a call like this:

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()进行的效果完全相同:

The effect is exactly the same as if you did this with 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())返回的环境时,由于其他原因会出现错误.在这种情况下,您的代码块最终将通过如下调用进行评估:


Your second example

When envir= is an environment returned by as.environment(list()), you get errors for a different reason. In that case, your code block ultimately gets evaluated by a call like this:

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是环境而不是列表时,不使用enclos参数.)

As you've noted, this fails because as.environment() returns an environment whose enclosing environment is the empty environment (i.e. the environment returned by emptyenv()). evaluate() (like eval() would) looks for the symbol <- in env and, when it doesn't find it there, starts up the chain of enclosing environments which, here, don't contain any match. (Recall also that when envir is an environment, rather than a list, the enclos argument is not used.)

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

To do what you want, you'll need to create an environment that: (1) contains all of the objects in your list and that; (2) has as its enclosing environment the parent environment of your call to render() (i.e. the environment in which a call to render() is normally evaluated). The most succinct way to do that is to use the nifty list2env() function, like so:

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

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

Doing so will result in your code chunks being evaluated by code like the following, which is what you want:

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!"

这篇关于在受限环境中使用`rmarkdown :: render`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆