如何在knitr子文档中隐藏和传递变量?

时间:2015-12-28 18:28:05

标签: r knitr

knitr使代码中的PDF成为(在我的情况下)R和LaTEX的组合。可以用儿童文件组装文件。

正如我现在使用的那样,文档是由全局变量组合而成,传入和传出每个子文档。这样可以很容易地生成意大利面条代码。

有没有办法让R变量成为子doc的“本地”?如何明确导出变量?

我可以在子doc的末尾清空每个局部变量,但我想知道是否有一些合理的正式机制来放松子文档之间的代码耦合。

1 个答案:

答案 0 :(得分:14)

knitr评估公共环境中的所有块(由knit_global()返回)。这是设计的;就像源文件中的所有代码都在同一环境中运行一样,所有块都在公共环境中执行。这同样适用于child documents,因为它们(原则上不是技术上)只是主文档的一部分,外部化为另一个文件。

这不一定会导致意大利面条代码:没有什么能阻止用户使用函数和其他对象来组织knitr文档中的代码/数据。但可能很少有用户这样做......

因此,没有用于块/子文档的封装机制的原因是它们假定以共享公共环境,因为它们是 one (main)的一部分文档。

但是, 可以包含子文档,使用户能够控制对象子文档和主文档共享。该解决方案基于函数knit_child(),它与chunk option child非常相似。直接调用knit_child()(通过child选项隐式调用)的优点是可以设置envir参数,该参数定义了代码块所在的环境。被评估" (来自?knit)。

knit_child()左右,我编写了包装器IsolatedChild来简化问题:

IsolatedChild <- function(input, ...) {

  evaluationEnv <- list2env(x = list(...), parent = as.environment(2))
  cat(asis_output(knit_child(input = input, envir = evaluationEnv, quiet = TRUE)))
  return(evaluationEnv)
}

传递给...的参数将在子文档中提供。 (将它们命名,请参见下面的示例。)该函数返回已评估子文档的环境。

parent中指定list2env至关重要,我根据this answer选择as.environment(2)。否则parent将默认为parent.frame(),从而将knit_global()中的对象公开给子文档。

assign可用于在全球环境中提供从IsolatedChild返回的对象。

请注意cat(asis_output())周围的knit_child构造,以确保子文档的输出正确包含在主文档中,而不管当前块中的results设置。

在转向这个例子之前,有两个最后的评论:

  • 如果孩子和主文档不应该共享任何对象,这种方法过于复杂。只需knit子文档并使用\include{}将其包含在主文档中。
  • 这种做法可能会带来一些陷阱。特别是孤立的孩子的封闭环境&#34;需要谨慎,因为搜索路径可能看起来与预期不同。请注意,主文档和子文档共享knitr个选项。此外,这两个文件都可以通过副作用进行互动(options()par(),打开的设备......)。

下面是完整的示例/演示:

  • 大块inputNormal没有什么特别之处,它只是对正常行为的证明。 inputHidden演示了IsolatedChild()的使用,将两个变量传递给子文档。
  • IsolatedChild()返回这两个值以及在子项中创建的第三个对象。
  • check演示了传递给&#34;隔离的孩子&#34;不要污染全球环境。
  • import显示assign如何用于&#34;导入&#34;来自孤立的孩子的对象&#34;对全球环境。
  

<强> main.Rnw

\documentclass{article}
\begin{document}

<<setup>>=
library(knitr)

objInMain <- TRUE

IsolatedChild <- function(input, ...) {

  evaluationEnv <- list2env(x = list(...), parent = as.environment(2))
  cat(asis_output(knit_child(input = input, envir = evaluationEnv, quiet = TRUE)))
  return(evaluationEnv)
}

@

<<inputNormal, child="child_normal.Rnw">>=
@

<<inputHidden, results = "asis">>=

returned <- IsolatedChild(input = "child_hidden.Rnw",
                          passedValue = 42,
                          otherPassedValue = 3.14)
cat(sprintf("Returned from hidden child: \\texttt{%s}",
            paste(ls(returned), collapse = ", ")))
@

<<check, results = "asis">>=
cat(sprintf("In global evaluation environment: \\texttt{%s}",
            paste(ls(), collapse = ", ")))
@

<<import, results = "asis">>=
assign("objInChildHidden", returned$objInChildHidden)
cat(sprintf("In global evaluation environment: \\texttt{%s}",
            paste(ls(), collapse = ", ")))
@
\end{document}
  

<强> child_normal.Rnw

<<inChildNormal>>=
objInChildNormal <- TRUE # visible in main.Rnw (standard behaviour)
@
  

<强> child_hidden.Rnw

Text in \texttt{child\_hidden.Rnw}.

<<inChildHidden>>=
objInChildHidden <- TRUE

print(sprintf("In hidden child: %s",
              paste(ls(), collapse = ", ")))


# Returns FALSE.
# Would be TRUE if "parent" weren't specifiet in list2env().
exists("objInMain", inherits = TRUE)
@
  

<强> main.pdf

main.pdf