Ответ 1
Два ваших первых примера не подходят по разным причинам. Чтобы понять оба отказа, сначала важно знать немного о том, как фрагменты кода оцениваются knitr и rmarkdown.
Процедура оценки фрагмента кода пользователя
Когда вы вызываете rmarkdown::render()
в свой файл, каждый фрагмент кода в конечном счете оценивается вызовом для evaluate::evaluate()
. С точки зрения его поведения оценки и правил определения масштаба, evaluate()
ведет себя почти так же, как базовая функция R eval()
.
(Где evaluate::evaluate()
отличается от eval()
тем, как обрабатывается вывод каждого оцениваемого выражения. Как объяснено в ?evaluate
, помимо оценки выражения, переданного в качестве первого аргумента, он "захватывает все информацию, необходимую для воссоздания вывода, как если бы вы скопировали и вставляли код в R-терминал ". Эта информация включает в себя графики и предупреждения и сообщения об ошибках, поэтому она так удобна в пакете, как knitr !)
В любом случае возможный вызов функции evaluate()
, изнутри функции knitr:block_exec()
, выглядит примерно так:
evaluate::evaluate(code, envir = env, ...)
в котором:
-
code
- это вектор символьных строк, дающий (возможно, несколько) выражений, составляющих текущий фрагмент. -
env
- это значение, которое вы предоставили формальному аргументуenvir
в своем первоначальном обращении кrmarkdown::render()
.
Ваш первый пример
В вашем первом примере envir
- это список, а не среда. В этом случае оценка выполняется в локальной среде, созданной вызовом функции. Неразрешенные символы (как описано в обеих ?eval
и ?evaluate
) присматривают за первый в списке был приняты envir
, а затем в цепи сред, начиная с даваемым enclos
аргументом. Задания, в решающей степени, являются локальными для среды временной оценки, которая выходит из существования после завершения вызова функции.
Поскольку envir
evaluate()
работает по одному за символом вектора выражений, когда envir
- это список, переменные, созданные в одном из этих выражений, не будут доступны для использования в последующих выражениях.
Когда аргумент envir
для rmarkdown::render()
- это список, ваш код в конечном итоге оценивается по вызову следующим образом:
library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
'print(x)')
env <- list(y = 1:10)
evaluate(code, envir = env)
## Or, for prettier printing:
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## > print(x)
## Error in print(x): object 'x' not found
Эффект точно такой же, как если бы вы сделали это с помощью eval()
:
env <- list(y =1 :10)
eval(quote(x <- "don't you ignore me"), envir = env)
eval(quote(x), envir = env)
## Error in eval(quote(x), envir = env) : object 'x' not found
Ваш второй пример
Когда envir=
- среда, возвращаемая as.environment(list())
, вы получаете ошибки по другой причине. В этом случае ваш код в конечном итоге оценивается по вызову следующим образом:
library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
'print(x)')
env <- as.environment(list(y = 1:10))
evaluate(code, envir = env)
## Or, for prettier printing:
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## Error in x <- "don't you ignore me!": could not find function "<-"
## > print(x)
## Error in print(x): could not find function "print"
Как вы заметили, это не удается, потому что as.environment()
возвращает среду, окружение которой представляет собой пустую среду (т.е. среду, возвращаемую emptyenv()
). evaluate()
(например, eval()
будет искать символ <-
in env
и, когда он не найдет его там), запустит цепочку окружающих сред, которые здесь не содержат никакого совпадения. (Вспомните также, что, когда envir
- это среда, а не список, аргумент enclos
не используется.)
Рекомендуемое решение
Чтобы сделать то, что вы хотите, вам нужно создать среду, которая: (1) содержит все объекты в вашем списке и что; (2) имеет в качестве среды окружения родительскую среду вашего вызова render()
(т.е. среду, в которой обычно оценивается вызов render()
). Самый краткий способ сделать это - использовать list2env()
nifty list2env()
, например:
env <- list2env(list(y="hello"), parent.frame())
render('test.Rmd', output_format = "html_document",
output_file = 'test.html',
envir = env)
Это приведет к тому, что ваши фрагменты кода будут оцениваться по коду следующим образом: это то, что вы хотите:
library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
'print(x)')
env <- list2env(list(y = 1:10), envir = parent.frame())
evaluate(code, envir = env)
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## > print(x)
## [1] "don't you ignore me!"