I have written a function that sources files that contain scripts for other functions and stores these functions in an alternative environment so that they aren't cluttering up the global environment. The code works, but contains three instances of 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")
}
Much has been written about the sub-optimality of the eval(parse(...))
construction (see here and here). However, the discussions that I've found mostly deal with alternate strategies for subsetting. The first and third instances of eval(parse(...))
in my code don't involve subsetting (the second instance might be related to subsetting).
Is there a way to call new.env(...)
, [environment name]$[function name] <- [function name]
, and attach(...)
without resorting to eval(parse(...))
? Thanks.
N.B.: I don't want to change the names of my functions to .name
to hide them in the global environment
Malicious code : invoking eval can crash a computer. For example: if you use eval server-side and a mischievous user decides to use an infinite loop as their username. Terribly slow : the JavaScript language is designed to use the full gamut of JavaScript types (numbers, functions, objects, etc)… Not just strings!
When creating evolutions models and simulations in R, code can quickly become messy and slow. Functions that require fewer lines of code can speed up your program, and make it easier to follow what is happening.
For what its worth, the function source
actually uses eval(parse(...))
, albeit in a somewhat subtle way. First, .Internal(parse(...))
is used to create expressions, which after more processing are later passed to eval
. So eval(parse(...))
seems to be good enough for the R core team in this instance.
That said, you don't need to jump through hoops to source functions into a new environment. source
provides an argument local
that can be used for precisely this.
local: TRUE, FALSE or an environment, determining where the parsed expressions are evaluated.
An example:
env = new.env()
source('test.r', local = env)
testing it works:
env$test('hello', 'world')
# [1] "hello world"
ls(pattern = 'test')
# character(0)
And an example test.r
file to use this on:
test = function(a,b) paste(a,b)
If you want to keep it off global_env, put it into a package. It's common for people in the R community to put a bunch of frequently used helper functions into their own personal package.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With