【问题标题】:Matching the knitr document environment in a custom engine在自定义引擎中匹配 knitr 文档环境
【发布时间】:2020-06-16 14:56:51
【问题描述】:

我有兴趣为 knitr 制作一个引擎,该引擎将在发送代码块进行评估之前通过以下步骤对代码块进行预处理:

  1. 预处理options$code
  2. 在父环境中使用evaluate::evaluate() 评估代码
  3. knitr::engine_output()格式化输出

我发现在这些自定义块中创建的任何变量都不会在文档的其余部分中可用。经过一番修改,我发现如果我遍历调用堆栈并找到调用 knitr 的最后一个位置,我可以将envir 参数的值用作evaluate::evaluate() 的环境。不过,这感觉很hacky。 有没有更好的方法来匹配自定义块引擎中的文档环境?

示例

在所有 cmets 上附加一个 Flippy 的引擎

knitr::knit_engines$set(flip =
  function(options) {
    # pre-process code
    code <- gsub("(#+?)", "\\1 (╯°□°)╯︵", options$code)

    # Find environment <--------------- IS THERE A BETTER WAY?
    #
    # grabbing the call stack
    cstack <- vapply(sys.calls(), function(i) paste(as.character(deparse(i)), collapse = "\n"), character(1))
    fstack <- sys.frames()
    # Finding the last instance of the knit function and grabbing the envir variable
    knitting <- rev(grep("knit(", cstack, fixed = TRUE))[1]
    e <- get("envir", fstack[[knitting]])

    OUT <- evaluate::evaluate(code, envir = e)
    knitr::engine_output(options, out = OUT)
  }
)

tmp <- tempfile(fileext = ".Rmd")
tmpout <- tempfile(fileext = ".md")
txt <- "---\noutput: md_document\n---\n\n```{r}\na <- 'A'\na\n```\n\n```{flip}\nb <- paste(a, 'and B') # FLIPPIN\nb\n```\n\nSponsored by the letters `r try(b)`\n"
cat(txt, file = tmp)

rmarkdown::render(tmp, output_file = tmpout, envir = new.env())
#> processing file: file3230dc4500b.Rmd
#> output file: file3230dc4500b.knit.md
#> /usr/bin/pandoc +RTS -K512m -RTS file3230dc4500b.utf8.md --to markdown_strict --from markdown+autolink_bare_uris+tex_math_single_backslash --output /tmp/Rtmpzc5qWO/file32306aeaf291.md --standalone
#> 
#> Output created: /tmp/Rtmpzc5qWO/file32306aeaf291.md
cat(readLines(tmp), sep = "\n")
#> ---
#> output: md_document
#> ---
#> 
#> ```{r}
#> a <- 'A'
#> a
#> ```
#> 
#> ```{flip}
#> b <- paste(a, 'and B') # FLIPPIN
#> b
#> ```
#> 
#> Sponsored by the letters `r try(b)`
cat(readLines(tmpout), sep = "\n")
#>     a <- 'A'
#>     a
#>     #> [1] "A"
#> 
#>     b <- paste(a, 'and B') # (╯°□°)╯︵ FLIPPIN
#>     b
#>     #> [1] "A and B"
#> 
#> Sponsored by the letters A and B

reprex package (v0.3.0) 于 2020 年 6 月 16 日创建

【问题讨论】:

  • 也许评估钩子(未记录)对您有用:github.com/yihui/knitr/blob/… 如果您添加自己的 evaluate() 函数,那么您甚至不需要创建引擎?
  • 评估钩子不会不分青红皂白地作用于所有 R 块吗?
  • 但感谢您将我指向这部分代码,因为我刚刚找到了knitr::knit_global(),这解决了我的问题!

标签: r r-markdown knitr environment evaluate


【解决方案1】:

事实证明,{knitr} 中有一个函数正是针对这种情况:knitr::knit_global(),恰好是评估内联代码时的环境:https://github.com/yihui/knitr/blob/0daf31be36eed8d8ec0ba51eedee909283afc45d/R/hooks.R#L13

knitr::knit_engines$set(flip = 
  function(options) {
    # pre-process code
    code <- gsub("(#+?)", "\\1 (╯°□°)╯︵", options$code)

    e <- knitr::knit_global() # <----- SOLUTION ᕕ( ᐛ )ᕗ

    OUT <- evaluate::evaluate(code, envir = e)
    knitr::engine_output(options, out = OUT)
  }
)

tmp <- tempfile(fileext = ".Rmd")
tmpout <- tempfile(fileext = ".md")
txt <- "---\noutput: md_document\n---\n\n```{r}\na <- 'A'\na\n```\n\n```{flip}\nb <- paste(a, 'and B') # FLIPPIN\nb\n```\n\nSponsored by the letters `r try(b)`\n"
cat(txt, file = tmp)

rmarkdown::render(tmp, output_file = tmpout, envir = new.env())
#> processing file: file3b0117b9cb1f.Rmd
#> output file: file3b0117b9cb1f.knit.md
#> /usr/bin/pandoc +RTS -K512m -RTS file3b0117b9cb1f.utf8.md --to markdown_strict --from markdown+autolink_bare_uris+tex_math_single_backslash --output /tmp/Rtmp1KgoUj/file3b01702d22f0.md --standalone
#> 
#> Output created: /tmp/Rtmp1KgoUj/file3b01702d22f0.md
cat(readLines(tmp), sep = "\n")
#> ---
#> output: md_document
#> ---
#> 
#> ```{r}
#> a <- 'A'
#> a
#> ```
#> 
#> ```{flip}
#> b <- paste(a, 'and B') # FLIPPIN
#> b
#> ```
#> 
#> Sponsored by the letters `r try(b)`
cat(readLines(tmpout), sep = "\n")
#>     a <- 'A'
#>     a
#>     #> [1] "A"
#> 
#>     b <- paste(a, 'and B') # (╯°□°)╯︵ FLIPPIN
#>     b
#>     #> [1] "A and B"
#> 
#> Sponsored by the letters A and B

reprex package (v0.3.0) 于 2020 年 6 月 16 日创建

【讨论】:

  • 我早上在浏览器中打开了这篇文章,本想输入knitr::knit_global(),但太忙了。现在我回到你的帖子,我很高兴你自己找到了它:)
  • 我正在开发一个自定义 knitr 函数。我读到knit_global() 默认返回R_GlobalEnv。在这种情况下,编织文档后,是否意味着ab在R Session中可用?我想要我的引擎,它通过在第一次调用时创建我的接口类的对象来工作,即第一个块,当knit() 完成时,我希望通过离开knit() 的范围来删除该对象。这是/你的方式吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
  • 2018-11-20
  • 2012-02-17
  • 2015-03-29
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多