我想编写一个S4对象,以便它可以传递给只接受S3对象的方法。 (似乎setOldClass()
可能与此有关,但documentation对我来说不清楚?)
e.g。一个最小的例子想象我有S3类和函数:
myS3 <- list(a = 1, b = 2)
class(myS3) <- "myS3class"
myS3function.myS3class <- function(x) x$a + x$b
我有S4对象
setClass("myS4class", representation(a = "numeric", b = "numeric"))
obj <- new("myS4class", a = 1, b = 2)
我有什么可以做的吗
myS3function.myS3class(obj)
给了我同样的东西
myS3function.myS3class(myS3)
通过仅修改S4类?
编辑这种方法的基本原理是利用S3类的所有现有方法(通常可能来自其他软件包等),而无需重写它们。我意识到一种方法只是编写一个强制方法(setAs()
),将我的S4对象转换为S3对象,但用户总是必须手动执行此步骤。 (虽然它有效,但我也有点不清楚使用setAs()
将S4类带入S3类是不好的做法,而不是在S4类之间进行映射)。
从我阅读setOldClass
的文档的方式来看,听起来这可以使S3对象像S4对象一样?那是对的吗?如果是这样,我的问题是,是否可以反过来(可能通过在S4类中设置prototype
?)。
如果这是不可能的,那么解释为什么这会是一个坏主意的理由呢?
答案 0 :(得分:2)
在S4类中添加一个方法,将其转换为S3类。
setGeneric(
"as.myS3class",
function(object)
{
standardGeneric("as.myS3class")
}
)
setMethod(
"as.myS3class",
signature(object = "myS4class"),
function(object)
{
structure(list(a = object@a, b = object@b), class = "myS3class")
}
)
然后你可以像这样调用S3方法:
myS3function(as.myS3class(obj))
答案 1 :(得分:0)
如果你想重用两个 S3和S4类的一个函数,而不是改变它,你可以为$
编写自己的定义:
f <- function(x, name)
slot(x, name)
setMethod("$", signature=c(x="myS4class"), definition=f)
myS3function.myS4class(obj)
# [1] 3
但这对我来说似乎很可疑。对于初学者,您可能还需要[[
的类似方法,因为函数可以引用列表元素:
setMethod("[[", signature=c(x="myS4class", i="character"),
definition=function(x, i) slot(x, i))
你也需要分配方法:
setMethod("$<-", signature=c(x="myS4class", value="numeric"),
definition=function(x, name, value) `slot<-`(x, name, check=TRUE, value))
setMethod("[[<-", signature=c(x="myS4class", i="character", value="numeric"),
definition=function(x, i, value) `slot<-`(x, i, check=TRUE, value))
但是你有数字引用的问题:
obj[[1]]
# Error in obj[[1]] : this S4 class is not subsettable
所以你需要另一种方法:
g <- function(x, i)
{
slots <- names(getClass("myS4class")@slots)
slot(x, slots[i])
}
setMethod("[[", signature=c(x="myS4class", i="numeric"), g)
总而言之,似乎需要做很多工作才能获得更多收益。
答案 2 :(得分:0)
成功的解决方案确实埋藏在setOldClass
:
## Examples of S3 classes with guaranteed attributes
## an S3 class "stamped" with a vector and a "date" attribute
## Here is a generator function and an S3 print method.
## NOTE: it's essential that the generator checks the attribute classes
stamped <- function(x, date = Sys.time()) {
if(!inherits(date, "POSIXt"))
stop("bad date argument")
if(!is.vector(x))
stop("x must be a vector")
attr(x, "date") <- date
class(x) <- "stamped"
x
}
print.stamped <- function(x, ...) {
print(as.vector(x))
cat("Date: ", format(attr(x,"date")), "\n")
}
## Now, an S4 class with the same structure:
setClass("stamped4", contains = "vector", representation(date = "POSIXt"))
## We can use the S4 class to register "stamped", with its attributes:
setOldClass("stamped", S4Class = "stamped4")
selectMethod("show", "stamped")
## and then remove "stamped4" to clean up
removeClass("stamped4")
someLetters <- stamped(sample(letters, 10),
ISOdatetime(2008, 10, 15, 12, 0, 0))
st <- new("stamped", someLetters)
st
# show() method prints the object's class, then calls the S3 print method.
stopifnot(identical(S3Part(st, TRUE), someLetters))
# creating the S4 object directly from its data part and slots
new("stamped", 1:10, date = ISOdatetime(1976, 5, 5, 15, 10, 0))
请注意,S4对象可以使用S3打印方法。让我感到惊讶的是,即使没有对selectMethod
的额外调用,这也适用于为S3类而不是S4类定义的其他方法。我在此处使用ape::phylo
对象的一个更详细的示例说明了这一点:http://carlboettiger.info/2013/10/07/nexml-phylo-class-extension.html