我有两个类(a
和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
和b + a
方法不一致,会导致错误。
> a + b
error in a + b : non-numeric argument for binary operator
additional: warning:
incompatible methods ("Ops.a", "Ops.b") for "+"
有没有办法使用S3正确定义所有四种情况?
答案 0 :(得分:9)
您可以将+.a
和+.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
和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"
更新:在玩这个游戏时我发现了一件事。如果你把它放在一个包中,你将得到“非数字参数”错误和“不兼容的运算符”警告。这是因为如果它们是完全相同的对象,并且在内存中具有相同的地址,则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)
}
在这种情况下,这两个方法指向内存中完全相同的对象,并且它可以工作(尽管它有点像黑客)。
答案 1 :(得分:4)
你不能使用那个策略。如您在帮助(Ops)页面中发现并记录的那样,它是特别禁止的。
“如果只为一个参数找到一个方法,或者两者都找到相同的方法,则使用它。如果找到不同的方法,则会出现关于'不兼容方法'的警告:在这种情况下或者如果没有方法是找到任何一个参数使用内部方法。“
因此,您需要将所有案例放入相同的方法中。 (经过测试并且确实成功了。)
答案 2 :(得分:1)
如果用反转的参数调用运算符呢?
Ops.b <- function(e1, e2){
if (class(e1) == "b" &
class(e2) == "b")
print("b & b")
if (class(e1) =="b" & class(e2)=="a")
e2+e1
NULL
}
但是我强烈建议使用适当的多次发送,因此S4就是这样。看到 Combining S4 and S3 methods in a single function 和 Adding S4 dispatch to base R S3 generic。