R语言 为不兼容的类定义S3组泛型

jaql4c8m  于 2023-04-09  发布在  其他
关注(0)|答案(1)|浏览(138)

比如,我正在实现一个自定义的S3类,名为“myclass”:

myvec <- 1:5
class(myvec) <- "myclass"

我定义了Group Ops泛型,以便在标准操作后保留类。

Ops.myclass <- function(e1, e2) {
  if(is(e1, "myclass")) {
    e1 <- unclass(e1)
    structure(NextMethod(), class="myclass")
  } else if(is(e2, "myclass")) {
    e2 <- unclass(e2)
    structure(NextMethod(), class="myclass")
  }
}

现在我的类被保留了,例如,加法:
然而,当我对有自己的Group泛型的对象这样做时,我遇到了问题:

myvec + Sys.Date()
[1] 19454 19455 19456 19457 19458
attr(,"class")
[1] "myclass"
Warning message:
Incompatible methods ("Ops.myclass", "+.Date") for "+"

它不仅产生警告,而且结果也不同:

unclass(myvec) + Sys.Date()
[1] "2023-04-07" "2023-04-08" "2023-04-09" "2023-04-10" "2023-04-11"

问题:如何解决这个警告,并使这个操作返回与myvec没有类时相同的结果?

基本上,我希望myclass有自己的组泛型,但在冲突的情况下,要服从,并在冲突时优先考虑其他类。

8cdiaqws

8cdiaqws1#

AFAICT,你在R〈4.3.0的时候运气不好。我打算建议定义S4方法,比如:

setOldClass("zzz")
setMethod("Ops", c("zzz", "zzz"), function(e1, e2) <do stuff>)
setMethod("Ops", c("zzz", "ANY"), function(e1, e2) <do stuff>)
setMethod("Ops", c("ANY", "zzz"), function(e1, e2) <do stuff>)

因为S4通用函数在S3分派之前执行S4分派(如果它们也是S3通用的)。
但是我记得Ops组的所有成员都是内部泛型的,所以当两个参数都不是S4对象时,就不会分派S4方法,就像<zzz> + <Date>的情况一样。
R 4.3.0引入了一个新的通用函数chooseOpsMethod,允许用户指定如何为Ops组的成员解决S3调度歧义。它在?Ops中有文档说明,当然也在?chooseOpsMethod中。我假设你已经阅读了相关章节,并建议如下:

.S3method("chooseOpsMethod", "zzz", 
          function(x, y, mx, my, cl, reverse) TRUE)
.S3method("Ops", "zzz",
          function(e1, e2) {
              if (inherits(e1, "zzz")) {
                  class(e1) <- NULL
                  cl <- oldClass(e2)
              } else {
                  class(e2) <- NULL
                  cl <- oldClass(e1)
              }
              r <- callGeneric(e1, e2)
              ## Do not assign a class to 'r' if the "other" argument inherits
              ## from a class with a method for this generic function ...
              if (is.null(cl) ||
                  (all(match(paste0(   "Ops", ".", cl), .S3methods(   "Ops"), 0L) == 0L) && 
                   all(match(paste0(.Generic, ".", cl), .S3methods(.Generic), 0L) == 0L)))
                  class(r) <- "zzz"
              r
          })
x <- structure(0:5, class = "zzz")
x + x
## [1]  0  2  4  6  8 10
## attr(,"class")
## [1] "zzz"
x + 0
## [1] 0 1 2 3 4 5
## attr(,"class")
## [1] "zzz"
0 + x
## [1] 0 1 2 3 4 5
## attr(,"class")
## [1] "zzz"
x + .Date(0L)
## [1] "1970-01-01" "1970-01-02" "1970-01-03" "1970-01-04" "1970-01-05" "1970-01-06"
.Date(0L) + x
## [1] "1970-01-01" "1970-01-02" "1970-01-03" "1970-01-04" "1970-01-05" "1970-01-06"

这是因为没有chooseOpsMethod.Date,而且chooseOpsMethod.default无条件返回FALSE。其他人可能会注册一个chooseOpsMethod.Date返回TRUE,破坏<Date> + <zzz>的行为,但这就是依赖S3而不是S4所冒的风险...

相关问题