[英]How do you hide and pass variables in knitr child documents?

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.


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


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.


1 个解决方案



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.


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 …


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.


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).


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)))

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.

傳遞給...的參數將在子文檔中提供。 (將它們命名,請參見下面的示例。)該函數返回已評估子文檔的環境。

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 can be used to make objects returned from IsolatedChild available in the global environment.


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:


  • 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.
  • 如果孩子和主文檔不應該共享任何對象,這種方法過於復雜。只需編織子文檔並使用\ include {}將其包含在主文檔中。
  • 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, ...).
  • 這種方法可能會帶來一些陷阱。特別是“孤立的孩子”的封閉環境需要謹慎,因為搜索路徑可能看起來與預期的不同。請注意,主文檔和子文檔共享knitr選項。此外,兩個文檔都可以通過副作用(options(),par(),打開的設備,......)進行交互。

Below a complete example / demo:


  • 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.
  • 塊inputNormal沒有什么特別之處,它只是對正常行為的演示。 inputHidden演示了IsolatedChild()的使用,將兩個變量傳遞給子文檔。
  • IsolatedChild() returns these two values along with a third object created in the child.
  • IsolatedChild()返回這兩個值以及在子項中創建的第三個對象。
  • check demonstrates that the objects passed to/created in the "isolated child" do not pollute the global environment.
  • check表明傳遞給“隔離子”的對象/創建的對象不會污染全局環境。
  • import shows how assign can be used to "import" an object from the "isolated child" to the global environment.
  • import顯示了如何使用assign將對象從“孤立子”導入到全局環境。





objInMain <- TRUE

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

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


<<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 = ", ")))



objInChildNormal <- TRUE # visible in main.Rnw (standard behaviour)



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

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)






粤ICP备14056181号  © 2014-2021 ITdaan.com