Ваши первые два примера не работают по разным причинам.Чтобы понять обе ошибки, сначала важно немного узнать о том, как куски кода оцениваются knitr и rmarkdown .
общая процедура оценки чанка кода knitr.
Когда вы вызываете 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
.Решающее значение имеют локальные назначения для среды временной оценки, которая прекращается после завершения вызова функции.
Поскольку 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()
) ищет символ <-
в env
и, когда он не находит его там, запускает цепочку окружающих сред, которые здесь не содержат совпадений.(Напомним также, что когда envir
является средой, а не списком, аргумент enclos
не используется.)
Рекомендуемое решение
Чтобы делать то, что вы хотите,вам нужно создать среду, которая: (1) содержит все объекты в вашем списке и что;(2) имеет в качестве окружающей среды родительскую среду вашего вызова render()
(т.е. среду, в которой обычно оценивается вызов render()
).Самый краткий способ сделать это - использовать изящную функцию 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!"