如何使 Ops 方法在非基础的两个对象之间兼容 class

How to make Ops method compatible between two objects of a non base class

假设我有一个 class“我的”,当它被添加到具有单位的对象(即来自 units 包)时,我想触发某些行为:

library(units)
my1 = structure(2, class="my")

Ops.my <- function(e1, e2=NULL) {
  ok <-
    switch(
      .Generic,
      `-` = ,
      `*` = ,
      `+` = ,
      '<=' = TRUE,
      FALSE
    )
  if (!ok) {
    stop(gettextf("%s not meaningful", sQuote(.Generic)))
  }
  get(.Generic)(as.integer(e1), as.integer(e2))
}

my1+set_units(5,nm)

目前,它给我以下警告:

Warning message:
Incompatible methods ("Ops.my", "Ops.units") for "+" 

但我实际上想以某种方式处理“我的”和“单位”的加法,我该怎么做? 我试过 Ops.my.units <- 之类的东西,但它似乎不起作用。

Ops 似乎没有办法做到这一点。来自文档:

The classes of both arguments are considered in dispatching any member of this group. For each argument its vector of classes is examined to see if there is a matching specific (preferred) or Ops method. If a method is found for just one argument or the same method is found for both, it is used. If different methods are found, there is a warning about ‘incompatible methods’

这可能是一件好事。 object-oriented 系统在像 R 这样的 non-compiled 语言中的部分好处是它有助于保持类型安全。这可以防止您不小心将苹果添加到橙子中,正如我们在以下示例中看到的那样:

apples  <- structure(2, class = "apples")

oranges <- structure(2, class = "oranges")

Ops.apples <- function(e1, e2) {
  value <- do.call(.Generic, list(as.integer(e1), as.integer(e2)))
  class(value) <- "apples"
  value
}

Ops.oranges <- function(e1, e2) {
  value <- do.call(.Generic, list(as.integer(e1), as.integer(e2)))
  class(value) <- "oranges"
  value
}

apples + apples
#> [1] 4
#> attr(,"class")
#> [1] "apples"

oranges + oranges
#> [1] 4
#> attr(,"class")
#> [1] "oranges"

apples + oranges
#> [1] 4
#> attr(,"class")
#> [1] "apples"
#> Warning message:
#>   Incompatible methods ("Ops.apples", "Ops.oranges") for "+" 

你可以看到,即使在这里,我们也可以忽略警告。

suppressWarnings(apples + oranges)
#> [1] 4
#> attr(,"class")
#> [1] "apples"

但希望您能明白为什么这可能不太好 - 我们添加了 2 个苹果和 2 个橙子,并返回了 4 个苹果。

在整个 R 及其扩展包中,有许多 type-conversion 函数,例如 as.integeras.numericas.logicalas.characteras.difftime 等。这些允许在类型之间转换和对不同类型执行操作时进行一些控制。

做这种事情的“正确”方法是专门将一种对象类型转换为另一种对象类型以执行操作:

as.my <- function(x) UseMethod("as.my")

as.my.default <- function(x) {
  value <- as.integer(x)
  class(value) <- 'my'
  value
}

my1 + as.my(set_units(5,nm))
#> [1] 7