在 rmarkdown/knitr 中使用 .Last.value

using .Last.value in rmarkdown/knitr

具有以下内容的 Rmd 文件:

```{r}
data.frame(a=1)
str(.Last.value)
```

它渲染 data.frame 但渲染 str(.Last.value) 产生 ## NULL.
是否有任何 knitr 选项或技巧可以使它按预期工作?
第二个的预期输出

## 'data.frame':    1 obs. of  1 variable:
##  $ a: num 1

因为所有代码块都是通过 eval()knitr 中计算的,所以代码块的最后一个表达式不是 top-level R 表达式,并且 .Last.value 不适用于 knitr.

为了更清楚地说明这一点:

x = 1
x  # a top-level expression if typed in the R console

相比之下,x 不再是 eval() 中的顶级表达式:

some_internal_knitr_function() {
  # internal code
  eval(parse(text = c('x = 1', 'x')))
  # more internal code
}

实际上,有一种方法可以使 .Last.value 起作用。

```{r setup, include=FALSE}
knitr::opts_chunk$set(render = function(x, ...){
    unlockBinding(".Last.value", .BaseNamespaceEnv)
    assign(".Last.value", x, .BaseNamespaceEnv)
    lockBinding(".Last.value", .BaseNamespaceEnv)
    knitr::knit_print(x, ...)
})
```

这段代码定义了一个自定义渲染函数,它将输出绑定到 .Last.value

不过,上述修复并不完美。该行为与控制台略有不同。例如,在 R console

> x <- 2
> .Last.value
[1] 2

.Last.value returns 最后一个值,即使上一个命令是不可见的。另一方面,上述针对 knitr 的 hack 要求打印结果。

```{r}
x <- 2
x
.Last.value
```

更新

要完全使 .Last.value 像在 R 控制台中一样工作,您可以考虑以下覆盖 knitr 内部函数的高风险 hack。

```{r setup, include=FALSE}
unlockBinding("knit_handlers", getNamespace("knitr"))
evalq(
  assign(
    "knit_handlers", 
    function(fun, options) {
      if (!is.function(fun)) fun = knit_print
      if (length(formals(fun)) < 2)
        stop("the chunk option 'render' must be a function of the form ",
             "function(x, options) or function(x, ...)")
      merge_list(default_handlers, list(value = function(x, visible) {
        unlockBinding(".Last.value", .BaseNamespaceEnv)
        assign(".Last.value", x, .BaseNamespaceEnv)
        lockBinding(".Last.value", .BaseNamespaceEnv)
        if (visible) fun(x, options = options)
      }))
     }
  ), 
  getNamespace("knitr")
)
lockBinding("knit_handlers", getNamespace("knitr"))
```

这会覆盖 knitr 内部函数 knit_handlers,因此它随时可能中断。