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.
knitr使用代碼組成PDF,這些代碼是(在我的情況下)R和LaTEX的組合。可以從子文檔中組裝文檔。
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?
有沒有辦法讓R變量“本地”到子doc?如何明確導出變量?
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.
我可以在子doc的末尾清空每個局部變量,但我想知道是否有一些合理的正式機制來放松子文檔之間的代碼耦合。
11
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評估公共環境中的所有塊(由knit_global()返回)。這是設計的;就像源文件中的所有代碼都在同一環境中運行一樣,所有塊都在公共環境中執行。這同樣適用於子文檔,因為它們(原則上,在技術上)不僅僅是主文檔的一部分,而是外部化到另一個文件。
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 …
這並不一定會導致意大利面條代碼:沒有什么能阻止用戶使用函數和其他對象來組織knitr文檔中的代碼/數據。但可能很少有用戶這樣做......
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
).
但是,可以以允許用戶控制對象子文檔和主文檔共享的方式包括子文檔。該解決方案基於函數knit_child(),它與塊選項子元素非常相似。直接調用knit_child()(通過子選項隱式調用)的優點是可以設置envir參數,該參數定義“評估代碼塊的環境”(來自?knit)。
Around knit_child()
, I wrote the wrapper IsolatedChild
to simplify matters:
圍繞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)
}
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.
在list2env中指定parent是至關重要的,我根據這個答案選擇了as.environment(2)。否則,parent將默認為parent.frame(),從而將knit_global()中的對象暴露給子文檔。
assign
can be used to make objects returned from IsolatedChild
available in the global environment.
assign可用於使從IsolatedChild返回的對象在全局環境中可用。
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.
請注意knit_child周圍的cat(asis_output())構造,它確保子文檔的輸出正確包含在主文檔中,而不管當前塊中的結果設置如何。
Before turning to the example, two final remarks:
在轉到示例之前,最后兩個評論:
knit
the child document and use \include{}
to include it in the main document.knitr
options. Besides, both documents could interact via side effects (options()
, par()
, opened devices, ...).Below a complete example / demo:
下面是一個完整的示例/演示:
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:
本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2015/12/28/725d50e02e96020b4b997b6e79fa598e.html。