如何在knitr子文档中隐藏和传递变量? [英] How do you hide and pass variables in knitr child documents?

查看:78
本文介绍了如何在knitr子文档中隐藏和传递变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

knitr 使用R和LaTEX的组合代码制作PDF.可以从子文档中组合文档.

knitr makes PDFs out of code that is a combination of (in my case) R and LaTEX. One can assemble a document out of child documents.

当我现在使用它时,该文档是由全局变量组装而成的,并传入和传出每个子文档.这样可以轻松生成意大利面条代码.

As I use it right now, the document is assembled out of global variables, passed into and out of every child document. This makes it easy to produce spaghetti code.

有什么办法可以使R变量对子文档本地化"?如何明确导出变量?

Is there any way to make R variables "local" to a child doc? How about make exporting of variables explicit?

我可以在子文档的末尾使每个局部变量为NULL,但是我想知道是否存在某种合理的形式化机制来放松子文档之间的代码耦合.

I could NULL out every local variable at the end of a child doc, but I wonder if there is some reasonable formal mechanism for loosening code coupling between child documents.

推荐答案

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

knitr evaluates all chunks in a common environment (returned by knit_global()). This is by design; just like all code in a source file runs in the same environment, all chunks are executed in a common environment. The same applies to child documents because they are (in principle, not technically) just a part of the main document, externalized to another file.

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

This does not necessarily lead to spaghetti code: Nothing prevents users from using functions and other objects to organize code/data in knitr documents. But probably few users do so …

因此,对于块/子文档没有封装机制的原因是,由于它们属于 one (主要)的一部分,因此它们被假定共享一个公共环境.文档.

So the reason why there are no encapsulation mechanisms for chunks/child documents is that they are supposed to share a common environment as they are part of one (main) document.

但是,可以 以允许用户控制对象的子文档和主文档共享的方式包含子文档.该解决方案基于功能knit_child(),该功能与块选项 child非常相似.直接调用knit_child()的优势(相对于通过child选项隐式调用)的优点是可以设置envir参数,该参数定义将在其中评估代码块的环境"(来自?knit)

However, it is possible to include child documents in a way that gives the user control over the objects child documents and the main document share. The solution is based on the function knit_child() which is very similar to the chunk option child. The advantage of calling knit_child() directly (vs. implicitly via the child option) is the possibility to set the envir argument which defines "the environment in which the code chunks are to be evaluated" (from ?knit).

knit_child()周围,我编写了包装器IsolatedChild来简化事情:

Around knit_child(), I wrote the wrapper IsolatedChild to simplify matters:

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

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

传递给...的参数将在子文档中可用. (为它们命名,请参见下面的示例.)该函数返回在其中评估了子文档的环境.

Arguments passed to ... will be available in the child document. (Name them, see example below.) The function returns the environment in which the child document has been evaluated.

list2env中指定parent是至关重要的,我根据此答案选择了as.environment(2).否则,parent将默认为parent.frame(),从而将knit_global()中的对象暴露给子文档.

Specifying parent in list2env is crucial and I chose as.environment(2) according to this answer. Otherwise parent would default to parent.frame(), thus exposing objects in knit_global() to the child document.

assign可用于使从IsolatedChild返回的对象在全局环境中可用.

assign can be used to make objects returned from IsolatedChild available in the global environment.

请注意knit_child周围的cat(asis_output())构造,这可以确保子文档的输出正确包含在主文档中,而与当前块中的results设置无关.

Note the cat(asis_output()) construction around knit_child which ensures that the output from the child document is correctly included in the main document, regardless of the results setting in the current chunk.

在转到示例之前,有两个最后要点:

Before turning to the example, two final remarks:

  • 如果子文档和主文档不应该共享任何对象,则此方法过于复杂.只需knit子文档,然后使用\include{}将其包含在主文档中即可.
  • 这种方法可能会带来一些陷阱.特别是孤立的孩子"的封闭环境需要特别注意,因为搜索路径可能看起来与预期的有所不同.请注意,主文档和子文档共享knitr选项.此外,两个文档都可以通过副作用(options()par(),打开的设备...)进行交互.
  • If the child and the main document are not supposed to share any objects, this approach is overly complex. Simply knit the child document and use \include{} to include it in the main document.
  • This approach might come with some pitfalls. Especially the enclosing environment of the "isolated child" needs caution because the search path might look different than expected. Note that main and child document share knitr options. Besides, both documents could interact via side effects (options(), par(), opened devices, ...).

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

Below a complete example / demo:

  • inputNormal并没有什么特别的,它只是正常行为的演示. inputHidden演示了IsolatedChild()的用法,将两个变量传递给子文档.
  • IsolatedChild()返回这两个值以及在子对象中 创建的第三个对象.
  • check演示传递给隔离子"中/在隔离子"中创建的对象不会污染全局环境.
  • import显示了如何使用assign将对象从隔离子"导入"到全局环境.
  • The chunk inputNormal does nothing special, it's just a demonstration of the normal behavior. inputHidden demonstrates the use of IsolatedChild(), passing two variables to the child document.
  • IsolatedChild() returns these two values along with a third object created in the child.
  • check demonstrates that the objects passed to/created in the "isolated child" do not pollute the global environment.
  • import shows how assign can be used to "import" an object from the "isolated child" to the global environment.

main.Rnw:

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:

child_normal.Rnw:

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

child_hidden.Rnw:

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:

这篇关于如何在knitr子文档中隐藏和传递变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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