%*%
运算符是内部通用的,这意味着调度是用C代码进行的.当前(即,在R 4.2.3中),对应的C函数do_matprod
(定义here)包含该判断:
if (PRIMVAL(op) == 0 && /* %*% is primitive, the others are .Internal() */
(IS_S4_OBJECT(x) || IS_S4_OBJECT(y))
&& R_has_methods(op)) {
SEXP s, value;
/* Remove argument names to ensure positional matching */
for(s = args; s != R_NilValue; s = CDR(s)) SET_TAG(s, R_NilValue);
value = R_possible_dispatch(call, op, args, rho, FALSE);
if (value) return value;
}
如果x
和y
都不是S4对象,如您的示例所示,则do_matprod
继续将它们作为传统矩阵处理,not查看任一参数的class
属性.你所指的第help("%*%")
节:
该运算符是S4通用的,但不是S3通用的.需要为名为x
和y
的两个参数的函数编写S4方法.
试图表达这一点,但不是特别清楚.(毕竟,您did定义了S4方法.)
这里有两个主要问题:
话虽如此,R-core已经promise 让%*%
运营商S3成为通用in the near future.如果发生这种情况,则%*%
的行为将类似于+
和Ops
组的其他内部通用成员,因为S3方法%*%.zzz
将在适当的地方被调度.但是,当两个参数都不是S4对象时,S4方法仍然不会被调度.
可以说,当您try 定义永远不会被调度的S4方法时,应该更改setMethod
以发出警告或错误的信号,就像您在示例中所做的那样.
附录
列举泛型函数的类型和它们分派的方法类型可能会有所帮助,从而将注意力限制在S3和S4上.我们将使用此脚本为我们的测试定义对象,每个对象都应该在一个新的R进程中运行:
## objects.R
w <- structure(0, class = "a")
x <- structure(0, class = "b")
setOldClass("c")
y <- structure(0, class = "c")
setClass("d", contains = "numeric")
z <- new("d", 0)
Non-internally S3 generic functions
这些方法通过UseMethod
为S3类和S4类分派S3方法.当没有找到任何方法时,它们分派默认方法*.default
或(如果没有找到)抛出错误.他们从不发送S4方法.
source("objects.R")
h <- .__h__. <- function(x) UseMethod("h")
.S3method("h", "default", function(x) "default")
.S3method("h", "a", function(x) "a3")
setMethod("h", "c", function(x) "c4")
setMethod("h", "d", function(x) "d4")
h <- .__h__. # needed to undo side effect of 'setMethod'
h
## function(x) UseMethod("h")
h(w)
## [1] "a3"
h(x)
## [1] "default"
h(y)
## [1] "default"
h(z)
## [1] "default"
Non-internally S4 generic functions
这些方法通过standardGeneric
为S3类(正式定义为setOldClass
)和S4类分派S4方法.当没有找到方法时,它们分派默认方法*@default
.如果默认方法是S3泛型,则再次进行分派,这一次分派给任何可用的S3方法.然而,通常默认方法只是调用stop
来发出错误信号.
source("objects.R")
h <- function(x) UseMethod("h")
.S3method("h", "default", function(x) "default")
.S3method("h", "c", function(x) "c3")
setMethod("h", "d", function(x) "d4")
h
## standardGeneric for "h" defined from package ".GlobalEnv"
##
## function (x)
## standardGeneric("h")
## <environment: 0x1044650b0>
## Methods may be defined for arguments: x
## Use showMethods(h) for currently available ones.
h@default
## Method Definition (Class "derivedDefaultMethod"):
##
## function (x)
## UseMethod("h")
##
## Signatures:
## x
## target "ANY"
## defined "ANY"
h(w)
## [1] "default"
h(x)
## [1] "default"
h(y)
## [1] "c3"
h(z)
## [1] "d4"
Internally generic functions
这些都在base中定义为原始函数.您可以参考帮助页面或源代码来确定它们是仅S3通用、仅S4通用,还是同时是S3和S4通用.在第三种情况下,只有在没有找到合适的S4方法的情况下才会进行S3分派.而且,正如我已经解释过的,只有当签名中的一个参数是S4对象时,才会发生S4分派.
让我们以+
和%*%
为例.两者都是S4通用的,但只有+
是S3通用的.
source("objects.R")
.S3method("+", "default", function(e1, e2) "default")
.S3method("+", "a", function(e1, e2) "a3")
.S3method("+", "b", function(e1, e2) "b3")
.S3method("+", "c", function(e1, e2) "c3")
.S3method("+", "d", function(e1, e2) "d3")
setMethod("+", c("c", "c"), function(e1, e2) "c4")
setMethod("+", c("d", "d"), function(e1, e2) "d4")
w + w
## [1] "a3"
x + x
## [1] "b3"
y + y
## [1] "c3"
z + z
## [1] "d4"
在这里,S3方法被调度.前三个结果是通过S3调度获得的.最终结果通过S4调度获得.
source("objects.R")
.S3method("%*%", "default", function(x, y) "default")
.S3method("%*%", "a", function(x, y) "a3")
.S3method("%*%", "b", function(x, y) "b3")
.S3method("%*%", "c", function(x, y) "c3")
.S3method("%*%", "d", function(x, y) "d3")
setMethod("%*%", c("c", "c"), function(x, y) "c4")
setMethod("%*%", c("d", "d"), function(x, y) "d4")
w %*% w
## [,1]
## [1,] 0
x %*% x
## [,1]
## [1,] 0
y %*% y
## [,1]
## [1,] 0
z %*% z
## [1] "d4"
在这里,S3方法是not个分派的.前三个结果是通过内部定义的默认方法获得的.最终结果通过S4调度获得.