R: Оценка скрипта в среде
Я хотел бы загрузить библиотечную функцию в сценарии, оцененном в указанной среде.
Пример:
## foo.R
## -----
## blah blah
library(extrafont)
loadfonts()
Предполагая для удобства среду оценки, является базовой средой:
sys.source("foo.R")
## Registering fonts with R
## Error in eval(expr, envir, enclos) : could not find function "loadfonts"
Замена loadfonts()
с extrafont:::loadfonts()
работает лучше, но все равно дает:
Error in get(as.character(FUN), mode = "function", envir = envir) :
object 'pdfFonts' of mode 'function' was not found
так как loadfonts()
требует pdfFonts()
определяется в grDevices
,
1 ответ
Это не совсем удовлетворительный ответ и длинный комментарий к @waterling.
Предлагаемое решение:
e<- new.env()
source("foo.R", local=e)
т.е.
source("foo.R", local=new.env())
что по существу эквивалентно:
sys.source("foo.R", envir=new.env())
Это работает по той же причине, почему:
sys.source("foo.R", envir=as.environment("package:grDevices"))
Как сообщается в ошибке (см. Вопрос), функция не найдена, pdfFonts()
является частью пакета grDevices
sys.source
выше выполняет скрипт в package:grDevices
среда, следовательно, функция найдена. Вместо этого по умолчанию sys.source(..., envir=baseenv())
и базовая среда предшествует grDevices
, следовательно pdfFonts()
не найден.
Первая проблема заключается в том, что я заранее не знаю, какие функции будут в моем скрипте. В этом случае настройка envir=new.env()
это более общий подход. По умолчанию new.env(parent=parent.frame())
поэтому у него один и тот же родитель sys.source()
, которая является глобальной средой. Таким образом, все видимое в глобальной среде видно в скрипте с sys.source(..., envir=new.env())
, то есть каждый объект, созданный пользователем и загруженными пользователем пакетами.
Проблема здесь в том, что мы больше не изолируем скрипт, что делает его менее воспроизводимым и стабильным. На самом деле, это зависит от того, что находится в памяти R в тот самый момент, когда мы называем sys.source
, Чтобы сделать вещи более практичными, это означает, foo.R
может работать только потому, что мы обычно называем это после bar.R
,
Вторая проблема заключается в том, что это не фактическое решение. Вопрос касается того, как запустить скрипт foo.R
в окружающей среде e
и при необходимости доступ к функциям, не принадлежащим e
, Принимая e
То, что (напрямую или через своих родителей) имеет доступ к этим функциям, на самом деле является обходным путем, а не решением.
Если этот тип обходного пути - единственный возможный путь, IMHO, лучше всего сделать так, чтобы он зависел только от стандартных пакетов R.
В начале R показывает:
search()
## [1] ".GlobalEnv" "package:stats" "package:graphics"
## [4] "package:grDevices" "package:utils" "package:datasets"
## [7] "package:methods" "Autoloads" "package:base"
это восемь официальных пакетов / сред.
Новые пакеты / среды, если явно не изменено значение по умолчанию, переходят во второй слот, и все те, что после первого, сдвигаются на одну позицию.
myEnv=new.env()
attach(myEnv)
search()
## [1] ".GlobalEnv" "myEnv" "package:stats"
## [4] "package:graphics" "package:grDevices" "package:utils"
## [7] "package:datasets" "package:methods" "Autoloads"
## [10] "package:base"
Таким образом, мы можем взять последние восемь в пути поиска, что означает, что первые восемь из них наследуют остальные. Нам нужно:
pos.to.env(length(search()) - 7)
## <environment: package:stats>
## attr(,"name")
## [1] "package:stats"
## attr(,"path")
## [1] "path/to//R/R-x.x.x/library/stats"
Следовательно:
sys.source("foo.R", envir=new.env(parent=pos.to.env(length(search()) - 7)))
или можно взять стандартный справочный пакет R, скажем stats
и его родители.
Следовательно:
sys.source("foo.R", envir=new.env(parent=as.environment("package:stats")))
ОБНОВИТЬ
Я нашел
РЕШЕНИЕ
Что касается сценария:
#foo.R
#-----
library(extrafont)
f=function() loadfonts()
environment(f) = as.environment("package:extrafont")
f()
Чтобы выполнить в новой среде:
sys.source("foo.R", envir=new.env(parent=baseenv()))
f()
теперь имеет доступ ко всем объектам в пакете extrafont
и те, которые загружены до этого.
В sys.source()
создавая new.env()
с любым родителем необходимо сделать environment()
задание на работу.