R data.table ':=' 在直接调用中工作,但包中的相同功能失败

R data.table ':=' works in direct call, but same function in a package fails

使用 R 的 data.table 包,

这个有效:

instruction = "a = data.table(name=1:3, value=1:3, blah=1:3); a[,c('value', 'blah'):=NULL]"
eval(parse(text=instruction))
#   name
#1:    1
#2:    2
#3:    3

这个有效:

myFunc = function(instruction) {
eval(parse(text=instruction))
}
myFunc(instruction)
#   name
#1:    1
#2:    2
#3:    3

现在,把这个函数打包,载入,尝试调用。这不起作用:

myFuncInPackage(instruction)
#Error in `:=`(c("value", "blah"), NULL) : 
#  Check that is.data.table(DT) == TRUE. Otherwise, := and `:=`(...) are defined for use in j, once only and in particular ways. See help(":=").

为什么?


编辑:@Roland 指出在 package Depends 字段中添加 data.table 使其工作。但是,我认为这不是一个很好的解决方案,因为该包并不真正依赖、要求或使用 data.table。我只想能够将 data.table 与包一起使用。

此外,data.table 的其他所有内容在函数中都可以正常工作,只是 := 运算符不行。

所以我想后续问题可能是:我是否应该将 data.table 添加到我编写的每个包的 Depends 中,以便 data.table 在该包的函数中按预期工作?这似乎不对...处理此问题的正确方法是什么?

我遇到了同样的问题,我通过将 data.table 添加到 ImportsDepends: 解决了这个问题。我的 data.table 版本是 1.9.6

我终于找到了这个问题的答案(几年后)。所有评论和答案都建议将data.table添加到DependsImports,但这是不正确的;该包不依赖于 data.table,并且假设它可以是任何包,而不仅仅是 data.table,这意味着逻辑结论,建议需要将所有可能的包添加到 Depends --因为该依赖项是由提供 instruction 的用户提供的,而不是由包提供的功能提供的。

相反,基本上,这是因为对eval的调用是在包的命名空间内完成的,这不包括其他包提供的功能。我最终通过在 eval 调用中指定全局环境解决了这个问题:

myFunc = function(instruction) {
eval(parse(text=instruction), envir=globalenv())
}

为什么这样有效

这会导致 eval 功能在搜索路径中包含必需包的环境中完成。

data.table 的情况下,由于函数重载的复杂性,调试起来特别困难。在这种情况下,罪魁祸首实际上不是 := 函数,而是 [ 函数。 := 错误是转移注意力的错误。在撰写本文时,data.table 中的 := 函数定义如下:

https://github.com/Rdatatable/data.table/blob/348c0c7fdb4987aa6da99fc989431d8837877ce4/R/data.table.R#L2561

":=" <- function(...) stop('Check that is.data.table(DT) == TRUE. Otherwise, := and `:=`(...) are defined for use in j, once only and in particular ways. See help(":=").')

就是这样。这意味着:任何对 := 作为函数的调用都会停止并显示一条错误消息,因为这不是作者打算使用 := 的方式。相反,:= 实际上只是由 data.table 中的 [ 函数解释的关键字。

但是这里发生了什么:如果 [ 函数没有正确映射到 data.table 指定的版本,而是映射到基础 [,那么我们有一个问题——因为它不能处理 :=,所以它被当作一个函数来处理并触发错误消息。所以罪魁祸首函数是 [.data.table —— 重载的括号运算符。

我的新包(包含 myFuncInPackage)发生了什么,当它去评估代码时,它将 [ 函数解析为基础 [ 函数而不是data.table[ 函数。它试图将 := 作为函数求值,但 [ 没有使用它,因为它不是正确的 [,因此 := 作为函数传递而不是作为 data.table 的值,因为 data.table 不在命名空间中(或者在 search() 层次结构中较低。在此设置中,:= 不被理解,因此它被评估为一个函数,因此触发上面 data.table 代码中的错误消息。

当您指定在全局环境中发生 eval 时,它将 [ 函数正确解析为 [.data.table,并且 := 被正确解释。

顺便说一句,如果您传递的不是字符串而是代码块(更好)到包内的 eval(),您也可以使用它:

eval(substitute(instruction), envir=globalenv())

这里,substitute 防止 instruction 在参数评估阶段在包命名空间内被(错误地)解析,这样它就可以完整地回到 globalenv 中使用所需的功能正确评估。