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() задание на работу.

Другие вопросы по тегам