使用 S3 的“+”方法模拟多重调度 - 可能吗?

2023-12-19

我有两节课(a and b)并且我想定义+方法给他们。 我需要针对两个类的四种可能组合使用不同的方法,即:

a + a  method 1
a + b  method 2
b + a  method 3
b + b  method 4

我知道我可以使用 S4 进行多次调度,但我想知道是否有办法使用 S3 模拟这种行为。我的方法如下:

a <- "b"
class(a) <- "a"

b <- "e"
class(b) <- "b"

Ops.a <- function(e1, e2){
  if (class(e1) == "a" &
      class(e2) == "a")
    print("a & a")
  if (class(e1) == "a" &
        class(e2) == "b")
    print("a & b")
  if (class(e1) == "b" &
        class(e2) == "a")
    print("b & a")
  NULL
}

a + a
a + b
b + a

所有这些都工作正常,但当然以下内容尚未定义。

b + b

现在为了涵盖这种情况,我添加了另一个方法定义。

Ops.b <- function(e1, e2){
  if (class(e1) == "b" &
        class(e2) == "b")
    print("b & b")
  NULL
}

这会导致b + b去工作但是现在a + b and b + a方法不一致就会造成错误。

> a + b
error in a + b : non-numeric argument for binary operator
additional: warning:
incompatible methods ("Ops.a", "Ops.b") for "+"

有没有办法使用 S3 正确定义所有四种情况?


你可以通过定义来做到这一点+.a and +.b作为相同的功能。例如:

a <- "a"
class(a) <- "a"
b <- "b"
class(b) <- "b"

`+.a` <- function(e1, e2){
  paste(class(e1), "+", class(e2))
}
`+.b` <- `+.a`

a+a
# [1] "a + a"
a+b
# [1] "a + b"
b+a
# [1] "b + a"
b+b
# [1] "b + b"

# Other operators won't work
a-a
# Error in a - a : non-numeric argument to binary operator

如果你定义Ops.a and Ops.b,它还将定义其他运算符的操作,可以通过以下方式访问.Generic在函数中:

##### Start a new R session so that previous stuff doesn't interfere ####
a <- "a"
class(a) <- "a"
b <- "b"
class(b) <- "b"

Ops.a <- function(e1, e2){
  paste(class(e1), .Generic, class(e2))
}

Ops.b <- Ops.a

a+a
# [1] "a + a"
a+b
# [1] "a + b"
b+a
# [1] "b + a"
b+b
# [1] "b + b"


# Ops covers other operators besides +
a-a
# [1] "a - a"
a*b
# [1] "a * b"
b/b
# [1] "b / b"

Update: 我在玩这个的时候还发现了一件事。如果将其放入包中,您将收到“非数字参数”错误和“不兼容的运算符”警告。这是因为,只有当多个运算符是完全相同的对象、在内存中具有相同的地址时,R 才可以使用多个运算符——但不知何故,在构建和加载包时,这两个函数会失去这个确切的标识。 (您可以通过使用来检查这一点pryr::address())

我发现有效的一件事是在加载包时显式注册 S3 方法。例如,这将放入您的包中:

# Shows the classes of the two objects that are passed in
showclasses <- function(e1, e2) {
  paste(class(e1), "+", class(e2))
}    

.onLoad <- function(libname, pkgname) {
  registerS3method("+", "a", showclasses)
  registerS3method("+", "b", showclasses)
}

在这种情况下,这两个方法指向内存中完全相同的对象,并且它可以工作(尽管它有点像黑客)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 S3 的“+”方法模拟多重调度 - 可能吗? 的相关文章

随机推荐