如何为基础矩阵S3子类编写%*%方法?解决方法未调用问题
为矩阵的S3子类编写%*%方法的问题
我想给基础矩阵的S3子类atm2d编写%*%方法,但遇到了问题:showMethods()显示对应签名的S4方法已存在,但调用%*%时该方法从未执行,导致乘法结果丢失了atm2d类属性。
测试对象与默认行为
创建测试对象:
x <- diag(3) class(x) <- c("atm2d", class(matrix())) print(x)
输出:
[,1] [,2] [,3] [1,] 1 0 0 [2,] 0 1 0 [3,] 0 0 1 attr(,"class") [1] "atm2d" "matrix" "array"
默认%*%会丢失类属性:
print(x %*% x)
输出:
[,1] [,2] [,3] [1,] 1 0 0 [2,] 0 1 0 [3,] 0 0 1
尝试的解决方案
我尝试编写保留类属性的%*%方法:
as.matrix.atm2d <- function(x, ...) { class(x) <- NULL x } matmult <- function(x, y) { v <- as.matrix(x) %*% as.matrix(y) class(v) <- c("atm2d", class(matrix())) v } methods::setOldClass("atm2d") # 也试过这种写法,同样无效 # methods::setOldClass(c("atm2d", class(matrix()))) methods::setMethod("%*%", c(x = "atm2d", y = "atm2d"), function(x, y) matmult(x, y))
showMethods()显示已创建预期签名的方法:
showMethods("%*%", class = "atm2d")
输出:
Function: %*% (package base) x="atm2d", y="atm2d"
但调用%*%时方法并未执行,结果还是丢失了类属性:
print(x %*% x)
输出:
[,1] [,2] [,3] [1,] 1 0 0 [2,] 0 1 0 [3,] 0 0 1
如果方法正常执行,应该得到带类属性的结果:
print(matmult(x, x))
输出:
[,1] [,2] [,3] [1,] 1 0 0 [2,] 0 1 0 [3,] 0 0 1 attr(,"class") [1] "atm2d" "matrix" "array"
问题解决方法
问题出在%*%的S4泛型调度逻辑上:当对象同时属于S3类和基础类(如matrix)时,S4会优先匹配基础类的方法,而非我们注册的S3类方法。按以下步骤修复:
正确注册S3类的继承关系
使用setOldClass时必须明确指定继承自matrix,这样S4才能正确识别类的层次:methods::setOldClass(c("atm2d", "matrix"))编写正确的S4方法
调整方法定义,调用基础矩阵的乘法后恢复类属性,同时确保调度优先级:methods::setMethod("%*%", signature(x = "atm2d", y = "atm2d"), function(x, y) { # 调用基础矩阵的%*%实现 res <- callNextMethod() # 为结果添加atm2d类属性 class(res) <- c("atm2d", class(res)) res })验证效果
再次执行乘法操作,此时自定义方法会被触发,结果保留atm2d类:print(x %*% x)输出:
[,1] [,2] [,3] [1,] 1 0 0 [2,] 0 1 0 [3,] 0 0 1 attr(,"class") [1] "atm2d" "matrix" "array"
注:由于%*%是S4泛型,S3方法不会被自动触发,必须通过S4注册的方式实现。
内容的提问来源于stack exchange,提问作者Trevor
相关产品推荐
相关产品推荐

