我定义了两个类,它们可以成功地添加两个自己的对象或一个数字和一个自己的对象。
a <- structure(list(val = 1), class = 'customClass1')
b <- structure(list(val = 1), class = 'customClass2')
`+.customClass1` <- function(e1, e2, ...){
val1 <- ifelse(is.numeric(e1), e1, e1$val)
val2 <- ifelse(is.numeric(e2), e2, e2$val)
val_res <- val1 + val2
print('customClass1')
return(structure(list(val = val_res), class = 'customClass1'))
}
`+.customClass2` <- function(e1, e2, ...){
val1 <- ifelse(is.numeric(e1), e1, e1$val)
val2 <- ifelse(is.numeric(e2), e2, e2$val)
val_res <- val1 + val2
print('customClass2')
return(structure(list(val = val_res), class = 'customClass1'))
}
print.customClass1 <- function(x, ...){
print(x$val)
}
print.customClass2 <- function(x, ...){
print(x$val)
}
a + a
# [1] 2
a + 1
# [1] 2
b + b
# [1] 2
1 + b
# [1] 2但是很明显,当我试图添加这两个自定义类时,就会出错。
a + b
# Error in a + b : non-numeric argument to binary operator
# In addition: Warning message:
# Incompatible methods ("+.customClass1", "+.customClass2") for "+" 我只可以为customClass1定义一个函数,但是当我尝试添加两个customClass2对象时,该函数将无法工作。有什么方法可以优先处理某项功能吗?
通过将我的函数优先于基本函数(例如,类型为数字或整数),r似乎自然地做到了这一点。当两个参数中有一个具有customClass类型时,R会自动将其重定向到我的函数,而不是默认函数。
发布于 2017-03-29 12:27:14
在?base::Ops的细节部分讨论了R如何选择调度方法
在分配这个组的任何成员时,都会考虑这两个参数的类。对于每个参数,将检查其类向量,以查看是否存在匹配的特定(首选)或“操作”方法。如果只为一个参数找到一个方法,或者为两个参数都找到相同的方法,则使用该方法。如果发现了不同的方法,就会有一个关于“不兼容方法”的警告:在这种情况下,或者如果没有为任何一个参数找到方法,则使用内部方法。
如果customClass1和customClass2是相关的,则可以使用虚拟类来允许使用这两个不同类的操作。例如,您可以混合POSIXct和POSIXlt,因为它们都是从POSIXt继承的。这在?DateTimeClasses中有记录。
"POSIXct"更方便地包含在数据帧中,而"POSIXlt"更接近于人类可读的表单.存在一个虚拟类"POSIXt",这两个类都从中继承:它用于允许诸如减法之类的操作将两者混合在一起。
例如:
class(pct <- Sys.time())
# [1] "POSIXct" "POSIXt"
Sys.sleep(1)
class(plt <- as.POSIXlt(Sys.time()))
# [1] "POSIXlt" "POSIXt"
plt - pct
# Time difference of 1.001677 secs如果这些类不是以这种方式关联的,那么Emulating multiple dispatch using S3 for “+” method - possible?的答案中就有一些很好的信息。
发布于 2017-03-29 12:51:18
约书亚解释了为什么在不构建虚拟超类和类似的情况下,您的方法在使用S3时永远无法顺利地工作。使用S3,您必须手动管理您使用的每一个可能的函数中的类分配。忘记分配超类一次,您就可以开始一个可以持续一段时间的bug搜索了。
我强烈建议放弃S3,搬到S4。然后,您可以为组“操作”定义两个方向的方法。这样做的优点是,现在为两个类定义了所有算术、逻辑和比较运算符。如果要将其限制为子组或单个运算符,请将“操作”替换为子组或运算符。有关帮助页?S4GroupGeneric的更多信息。
基于S3类的示例使用虚拟类来简化操作:
# Define the superclass
setClass("super", representation(x = "numeric"))
# Define two custom classes
setClass("foo", representation(slot1 = "character"),
contains = "super")
setClass("bar", representation(slot1 = "logical"),
contains = "super")
# Set the methods
setMethod("Ops",
signature = c('super','ANY'),
function(e1,e2){
callGeneric(e1@x, e2)
})
setMethod("Ops",
signature = c('ANY','super'),
function(e1,e2){
callGeneric(e1, e2@x)
})
# Redundant actually, but limits the amount of times callGeneric
# has to be executed.
setMethod("Ops",
signature = c('super','super'),
function(e1,e2){
callGeneric(e1@x, e2@x)
})
foo1 <- new("foo", x = 3, slot1 = "3")
bar1 <- new("bar", x = 5, slot1 = TRUE)
foo1 + bar1
#> [1] 8
bar1 + foo1
#> [1] 8
bar1 < foo1
#> [1] FALSE
foo1 / bar1
#> [1] 0.6有两个不同槽名的类的示例:
setClass("foo", representation(x = "numeric"))
setClass("bar", representation(val = "numeric"))
setMethod("Ops",
signature = c('foo','ANY'),
function(e1,e2){
callGeneric(e1@x, e2)
})
setMethod("Ops",
signature = c('bar','ANY'),
function(e1,e2){
callGeneric(e1@val, e2)
})
setMethod("Ops",
signature = c('ANY','bar'),
function(e1,e2){
callGeneric(e1, e2@val)
})
setMethod("Ops",
signature = c('ANY','foo'),
function(e1,e2){
callGeneric(e1, e2@x)
})同样,您可以使用上面的代码来检查结果。请注意,在这里,您将得到一个关于选择的方法时,交互尝试。为了避免这种情况,可以添加一个用于签名c('foo','bar')和c('bar','foo')的方法。
https://stackoverflow.com/questions/43066501
复制相似问题