Как мне избежать eval и разобрать?
Я написал функцию, которая создает исходные файлы, содержащие сценарии для других функций, и сохраняет эти функции в альтернативной среде, чтобы они не загромождали глобальную среду. Код работает, но содержит три экземпляра eval(parse(...))
:
# sourceFunctionHidden ---------------------------
# source a function and hide the function from the global environment
sourceFunctionHidden <- function(functions, environment = "env", ...) {
if (environment %in% search()) {
while (environment %in% search()) {
if (!exists("counter", inherits = F)) counter <- 0
eval(parse(text = paste0("detach(", environment, ")")))
counter <- counter + 1
}
cat("detached", counter, environment, "s\n")
} else {cat("no", environment, "attached\n")}
if (!environment %in% ls(.GlobalEnv, all.names = T)) {
assign(environment, new.env(), pos = .GlobalEnv)
cat("created", environment, "\n")
} else {cat(environment, "already exists\n")}
sapply(functions, function(func) {
source(paste0("C:/Users/JT/R/Functions/", func, ".R"))
eval(parse(text = paste0(environment, "$", func," <- ", func)))
cat(func, "created in", environment, "\n")
})
eval(parse(text = paste0("attach(", environment, ")")))
cat("attached", environment, "\n\n")
}
Много было написано о неоптимальности конструкции eval(parse(...))
(см. Здесь и здесь). Тем не менее, обсуждения, которые я нашел, в основном касаются альтернативных стратегий для поднабора. Первый и третий экземпляры eval(parse(...))
в моем коде не включают поднаборы (второй экземпляр может быть связан с поднабором).
Есть ли способ вызвать new.env(...)
, [environment name]$[function name] <- [function name]
и attach(...)
не прибегая к eval(parse(...))
? Благодарю.
NB. Я не хочу менять имена своих функций на .name
чтобы скрыть их в глобальной среде.
Ответы
Ответ 1
Для чего бы то ни было, source
функции на самом деле использует eval(parse(...))
, хотя и несколько тонким способом. Во-первых, .Internal(parse(...))
используется для создания выражений, которые после дальнейшей обработки передаются в eval
. Так что eval(parse(...))
кажется достаточно хорошим для основной команды R в этом случае.
Тем не менее, вам не нужно прыгать через обручи к исходным функциям в новой среде. source
предоставляет local
аргумент, который можно использовать именно для этого.
local: TRUE, FALSE или среда, определяющая, где анализируются выражения.
Пример:
env = new.env()
source('test.r', local = env)
тестирование работает:
env$test('hello', 'world')
# [1] "hello world"
ls(pattern = 'test')
# character(0)
И пример файла test.r
чтобы использовать это на:
test = function(a,b) paste(a,b)
Ответ 2
Если вы хотите оставить это вне global_env, поместите его в пакет. Люди из сообщества R обычно помещают кучу часто используемых вспомогательных функций в свой личный пакет.
Ответ 3
tl; dr: правильный способ преобразования строк в кавычках в имена объектов - это использовать assign()
и get()
. Смотрите этот пост.
Длинный ответ: Ответ @dww о возможности source()
напрямую в конкретную среду привел меня к изменению второго экземпляра eval(parse(...))
следующим образом:
# old version
source(paste0("C:/Users/JT/R/Functions/", func, ".R"))
eval(parse(text = paste0(environment, "$", func," <- ", func)))
# new version
source(
paste0("C:/Users/JT/R/Functions/", func, ".R"),
local = get(environment)
)
Ответ от @dww также заставил меня исследовать attach()
. attach()
есть аргумент, который позволяет указать среду, в которую следует направить вывод. Это привело меня к изменению третьего экземпляра eval(parse(...))
(ниже). Обратите внимание на использование get()
для преобразования "env"
которое приходит из environment
в env
, не заключенный в кавычки, которого требует attach()
.
# old version
eval(parse(text = paste0("attach(", environment, ")")))
# new version
attach(get(environment), name = environment)
Наконец, в какой-то момент этого процесса мне напомнили, что у rm()
есть аргумент character.only
. detach()
принимает тот же аргумент, поэтому я изменил второй экземпляр eval(parse())
как показано ниже:
# old version
eval(parse(text = paste0("detach(", environment, ")")))
# new version
detach(environment, character.only = T)
Итак, мой новый код:
# sourceFunctionHidden ---------------------------
# source a function and hide the function from the global environment
sourceFunctionHidden <- function(functions, environment = "env", ...) {
if (environment %in% search()) {
while (environment %in% search()) {
if (!exists("counter", inherits = F)) counter <- 0
detach(environment, character.only = T)
counter <- counter + 1
}
cat("detached", counter, environment, "s\n")
} else {cat("no", environment, "attached\n")}
if (!environment %in% ls(.GlobalEnv, all.names = T)) {
assign(environment, new.env(), pos = .GlobalEnv)
cat("created", environment, "\n")
} else {cat(environment, "already exists\n")}
sapply(functions, function(func) {
source(
paste0("C:/Users/JT/R/Functions/", func, ".R"),
local = get(environment)
)
cat(func, "created in", environment, "\n")
})
attach(get(environment), name = environment)
cat("attached", environment, "\n\n")
}