【问题标题】:How do I avoid eval and parse?如何避免评估和解析?
【发布时间】:2019-03-29 22:03:43
【问题描述】:

我编写了一个函数,它可以获取包含其他函数脚本的文件,并将这些函数存储在另一个环境中,这样它们就不会弄乱全局环境。该代码有效,但包含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(...)) 构造的次优性已经写了很多(参见here 和here)。但是,我发现的讨论主要涉及子集的替代策略。我的代码中eval(parse(...)) 的第一个和第三个实例不涉及子集(第二个实例可能与子集有关)。

有没有办法在不求助于eval(parse(...)) 的情况下调用new.env(...)、[environment name]$[function name] &lt;- [function name] 和attach(...)?谢谢。

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

【问题讨论】:

  • 刚刚发现eval(parse(text = paste0("detach(", environment, ")")))可以换成detach(environment, character.only = T)。关于改进eval(parse(text = paste0("attach(", environment, ")"))) 的问题仍然存在。

标签: r eval


【解决方案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)

【讨论】:

  • 谢谢,我错过了source() 的那方面。但是,如果我将该行代码更改为source(paste0("C:/Users/JT/R/Functions/", func, ".R"), local = environment),则会收到错误Error in source(paste0("C:/Users/JT/R/Functions/", func, ".R"), local = environment) : 'local' must be TRUE, FALSE or an environment。有没有办法将来自environment 的"env" 转换为env?
  • 你应该创建一个环境来保存。例如,正如我用env = new.env() 演示的那样。然后将环境作为您的参数传递。如果您需要使用字符串命名新环境(在您的示例中为environemt - 尽管使用保留字作为名称是不好的做法),您可以使用assign(environment, new.env())
【解决方案2】:

如果你想让它远离 global_env,请将它放入一个包中。 R 社区中的人们将一堆常用的辅助函数放入他们自己的个人包中是很常见的。

【讨论】:

  • 没你想的那么难!我认为您要编写的功能要困难得多,复杂得多。有很多教程可以在那里编写软件包。
  • 我还没有时间做一个包,但是如果this description 是多么的容易是准确的,天哪!我要把所有东西都打包成一个包!
【解决方案3】:

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

长答案:@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() 将来自environment 的"env" 转换为attach() 需要的未引用的env。

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

【讨论】:

    猜你喜欢
    • 2010-10-03
    • 2014-06-13
    • 2011-04-03
    • 2014-07-06
    • 1970-01-01
    • 2016-05-08
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多