Подтвердить что ты не робот

Knitr наследует переменные из пользовательской среды, даже с envir = new.env()

Я обнаружил, что документ knitr наследует переменные из пользовательской среды, даже если предоставлен аргумент envir = new.env(). Как я могу предотвратить наследование этих переменных?

Например, предположим, что я написал простой файл .Rmd, используя переменную, которая не существует (y), связала ее и показала результирующий файл:

library(knitr)
writeLines(c("```{r}", "y + 1", "```"), "test.Rmd")
knit("test.Rmd", quiet = TRUE, envir = new.env())
# [1] "test.md"
cat(readLines("test.md"), sep = "\n")
# 
# ```r
# y + 1
# #> Error in eval(expr, envir, enclos): object 'y' not found
# ```

Конечно, я получаю сообщение об ошибке, что переменная y не существует, как и должно быть.

Однако, если я затем определяю y в своей собственной среде, я могу теперь ссылаться на y в файле .Rmd, хотя я даю аргумент envir = new.env().

y <- 3
knit("test.Rmd", quiet = TRUE, envir = new.env())
# [1] "test.md"
cat(readLines("test.md"), sep = "\n")
#
# ```r
# y + 1
# # [1] 4
# ```

Мое понимание заключалось в том, что envir = new.env() должен был обработать документ knitr в новой среде без переменной y. Это проблема, потому что она позволяет документам knitr быть невоспроизводимыми, ссылаясь на переменные, которые я не определяю в документе.

Обратите внимание, что rmarkdown render документация (которая является оберткой вокруг knit), в частности говорит, что вы можете использовать envir = new.env():

Среда, в которой фрагменты кода должны оцениваться во время вязания (можно использовать new.env(), чтобы гарантировать пустую новую среду).

Однако, по этой же причине render показывает то же поведение, что и выше. Неужели мои ожидания (и rmarkdown docs) неправильны в отношении envir = new.env(), или я использую его неправильно? И есть ли другой способ гарантировать новую среду в документе, вязаном?

4b9b3361

Ответ 1

new.env имеет аргумент parent, по умолчанию - parent.frame() - то есть вызывающий. Другими словами, ваша новая среда наследует весь материал из вашей текущей среды.

Вы можете избежать этого, указав parent:

new.env(parent = baseenv())

Или, если вы хотите наследовать загруженные пакеты:

new.env(parent = as.environment(2))

И да, документация render несколько вводит в заблуждение: в то время как new.env() предоставляет новую, пустую среду, ее не полностью отделяет от вызывающего, а вызывающий абонент, вероятно, почти никогда не хочет использовать только new.env().

Чтобы иметь возможность использовать пакеты внутри чистой среды, унаследованной от baseenv(), вам необходимо вручную реализовать механизм вложения пакетов, поскольку пакеты R не поддерживают изолирование среды самостоятельно (grrr!). Или вы используете "modules" пакет, который поддерживает локально прикрепленные пакеты:

```{r}
modules::import_package('ggplot2', attach = TRUE)
qplot(rnorm(10))
```

Аргумент attach = TRUE приводит к локальному подключению пакета, в отличие от library.

Здесь есть урезанная версия кода загрузки "modules" , который может быть использован:

require_namespace = function (package) {
    ns = .Internal(getRegisteredNamespace(package))
    if (is.null(ns))
        ns = tryCatch(loadNamespace(package), error = identity)

    ns
}

exhibit_package_namespace = function (namespace, name, parent, export_list) {
    structure(list2env(sapply(export_list, getExportedValue, ns = namespace,
                              simplify = FALSE),
                       parent = parent.env(parent)),
              name = paste('package', name, sep = ':'),
              path = getNamespaceInfo(namespace, 'path'))
}

library_local = function (package, parent = parent.frame()) {
    pkg_ns = require_namespace(package)
    if (inherits(pkg_ns, 'error'))
        stop('Unable to load package ', sQuote(package), '\n',
             'Failed with error: ', sQuote(conditionMessage(pkg_ns)))

    export_list = getNamespaceExports(pkg_ns)
    pkg_env = exhibit_package_namespace(pkg_ns, package, parent, export_list)
    parent.env(parent) = pkg_env
}

Использование:

```{r}
library_local('ggplot2')
qplot(rnorm(10))
```