【问题标题】:Lazy evaluation for S4 method argumentsS4 方法参数的惰性求值
【发布时间】:2014-04-30 20:20:28
【问题描述】:

我正在实现一个包含 data.table 的 S4 类,并尝试实现对象的 [ 子集(如 here 所述),以便它也子集 data.table。例如(仅定义 i 子集):

library(data.table)

.SuperDataTable <- setClass("SuperDataTable", representation(dt="data.table"))

setMethod("[", c("SuperDataTable", "ANY", "missing", "ANY"),
    function(x, i, j, ..., drop=TRUE)
{
    initialize(x, dt=x@dt[i])
})

d = data.table(a=1:4, b=rep(c("x", "y"), each=2))
s = new("SuperDataTable", dt=d)

此时,使用数字向量 (s[1:2]) 进行子集化可以正常工作(它将插槽中的 data.table 子集化)。但是,我想添加使用表达式进行子集化的能力。这适用于data.table 本身:

s@dt[b == "x"]
#    a b
# 1: 1 x
# 2: 2 x

但不适用于 S4 [ 方法:

s[b == "x"]
# Error: object 'b' not found

问题似乎是 S4 方法签名中的参数没有使用 R 的传统惰性求值进行求值 - 请参阅 here

泛型函数签名中的所有参数都是 在调用函数时进行评估,而不是使用 S 的传统惰性求值规则。因此,重要的是 从签名中排除任何需要处理的参数 象征性地(例如函数替换的第一个参数)。

这解释了为什么它不起作用,但不是可以如何实现这种子集,因为ij 包含在泛型的签名中。有没有办法让i 参数立即被评估?

【问题讨论】:

  • 我想我很困惑。我通常认为初始化步骤是new 操作的一部分,而不是提取操作的一部分。
  • @IShouldBuyABoat initialization 的使用来自this suggestion(也链接到上面),因为initialization 是一个复制构造函数。如果您想用x@dt = x@dt[i]; return(x) 替换它,您可以:它与上述问题无关。
  • 如果 R 不知道参数的类别,它如何确定调用什么方法?
  • @hadley:如果该参数的签名是ANY,可以想象它会起作用,因此它不需要知道参数的类别(尽管我知道它不需要) t)。

标签: r oop data.table subset s4


【解决方案1】:

你可能在这件事上不走运。来自R developer notes

出现在泛型签名中的参数将在泛型函数后立即进行评估 叫做;因此,任何需要利用惰性求值的参数都不能在 签名。这些通常是字面处理的参数,通常通过substitute() 函数。 例如,如果想要将替代()本身变成一个泛型,第一个参数 expr, 不会出现在签名中,因为它不能被评估,而是被视为文字。

此外,由于方法缓存,

完整签名中的所有参数都如上所述进行评估,而不仅仅是活动的 那些。否则,在特殊情况下,函数的行为可能会改变一个 缓存另一个方法时的方法,这绝对是不可取的。

我将遵循 data.table 包编写者的示例并使用 S3 对象(请参阅他们的 source codeR/data.table.R 的第 304 行)。您的 S3 对象仍然可以在下面创建和操作 S4 对象以保持半静态类型功能。

我们不能变得特别聪明:

 ‘[’ is a primitive function;  methods can be defined, but the generic function is implicit, and cannot be changed.

同时定义 S3 和 S4 方法将分派 S3 方法,这使我们看起来应该能够绕过 S4 调用并手动分派它,但不幸的是,参数评估仍然发生!您可以通过借用 plyr::. 来接近,这将为您提供如下语法:

s <- new('SuperDataTable', dt = as.data.table(iris))
s[.(Sepal.Length > 4), 2]

不理想,但比其他任何东西都更接近。

【讨论】:

  • 感谢您的详细回答-您能否详细说明“在下面创建和操作 S4 对象”?你的意思是让 S3 对象有一个成员是 S4 对象(如s$myS4obj?)我试图让它在 S4 中工作的主要原因是由于Bioconductor's package standards
  • @DavidRobinson 您可以实现标准的 S3 接口,但要么将 S4 对象保存在 S3 对象的列表中,要么将其粘贴在属性中。确保编写一个print 方法,这样您的用户就不会看到内部的深处。我不确定 Bioconductor 的封装标准,但就像我说的,如果你真的需要一个纯 S4 对象,你可以使用上面的 .() 技巧——据我的研究显示,没有其他解决方案能给你带来什么不想尝试编写与 R 内部结构混淆的 C 代码。
猜你喜欢
  • 2015-08-21
  • 2018-12-21
  • 1970-01-01
  • 1970-01-01
  • 2018-09-30
  • 2019-12-09
  • 2011-02-06
  • 2012-08-31
  • 1970-01-01
相关资源
最近更新 更多