如何避免评估和分析?

时间:2019-03-29 22:03:43

标签: r 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(...))构造的次优性已有很多论述(请参见herehere)。但是,我发现的讨论主要涉及替代的子集策略。我的代码中eval(parse(...))的第一个和第三个实例不涉及子集(第二个实例可能与子集有关)。

有没有一种方法可以调用new.env(...)[environment name]$[function name] <- [function name]attach(...)而不求助于eval(parse(...))?谢谢。

N.B .:我不想将函数名称更改为.name以便将它们隐藏在全局环境中

3 个答案:

答案 0 :(得分:5)

函数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)

答案 1 :(得分:3)

如果要使其与global_env分开,请将其放入包装中。对于R社区中的人们来说,将一堆常用的辅助函数放入自己的个人包中是很常见的。

答案 2 :(得分:0)

tl; dr:将带引号的字符串转换为对象名称的正确方法是使用assign()get()。参见this post

冗长的答案:@dww关于能够直接source()到特定环境的回答导致我更改了eval(parse(...))的第二个实例,如下所示:< / p>

# 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")
}