我在R中开发了一个S3类,其行为与因子变量非常相似,但并不完全相同。我在实施中留下的唯一一个问题是factor
和as.factor
不是泛型。
我通过覆盖我的包中的base::factor
函数中的.onload
覆盖了我个人使用的限制,如下所示:
.onAttach <- function(libname,pkgname){
# note that as.factor is not a generic -- need to override it
methods:::bind_activation(on = TRUE)
# TODO: make a better attmept to deterime if base::factor is a generic or not.
if(!length(ls(pattern='^as\\.factor\\.default$', envir=as.environment('package:base'),all.names=TRUE))){
# bind the current implementation of 'as.factor' to 'as.factor.default'
assign('as.factor.default',
base:::as.factor,
envir=as.environment('package:base'))
# unock the binding for 'as.factor'
unlockBinding('as.factor', as.environment('package:base'))
# bind the generic to 'as.factor' in the 'package:base'
assign('as.factor',
function (x,...) UseMethod('as.factor') ,
envir=as.environment('package:base'))
# re-lock the binding for 'as.factor'
lockBinding('as.factor', as.environment('package:base'))
}
[similar code for making 'factor' and 'table' behave as generics excluded]
}
但是我知道修改base
永远不会在CRAN上飞行,所以我很好奇是否有解决方法。正如@BondedDust指出的那样,我当然可以将我的函数重命名为普通因子(目前命名为as.factor.MYCLASS
)的强制类似于As.factor
,但我宁愿不去那条路,因为这意味着用户必须编写如下代码:
#coerce x to a factor
if(inherits(x,'MYCLASS'))
x <- As.factor(x)
else
x <- as.factor(x)
或
if(inherits(x,'MYCLASS'))
x <- Factor(x)
else
x <- factor(x)
将因素强制作为一般因素并未实施,这感觉很奇怪。
我也尝试过.onAttach
.onAttach <- function(libname,pkgname){
setOldClass(c("MYCLASS"),
where=as.environment('package:MyPackage'))
setMethod('factor',
signature(x='MYCLASS'),
factor.MYCLASS,
where=as.environment('package:MyPackage'))
}
但我收到此错误消息:
Error in rematchDefinition(definition, fdef, mnames, fnames, signature) :
methods can add arguments to the generic ‘factor’ only if '...' is an
因为factor
不使用dots
参数而我的factor.MYCLASS
还有一个参数。
答案 0 :(得分:1)
绝对不需要替换base
函数。只需在包中覆盖它们即可使它们成为通用的。
所以,在你的包中,做:
factor = function (...)
UseMethod('factor')
factor.default = base::factor
factor.MyClass = function (...) your logic
由于您的程序包将在attach
之后base
进行修改,因此将首先找到此factor
重新定义。
答案 1 :(得分:0)
回答了我自己的问题。下面的代码替换了我的包中的原始.onLoad
函数。这并不能完全满足我的用户希望能够调用as.factor(obj,arg='arg')
obj
MYCLASS
是一个具有类.onLoad
的对象的愿望,所以我把代码放在原来的{{1}上面的方法到一个名为setGenerics()
的函数中,该函数根据用户的请求为factor
和as.factor
创建S3泛型。
我对此解决方案非常满意。我只是希望这能满足CRAN的要求。
# create a virtual S4 class from my S3 class
setOldClass(c("MYCLASS"))
# set methods for the virtual S4 classes of 'ordered','factor'
setMethod('as.ordered',
signature(x='MYCLASS'),
function(x)as.factor.MYCLASS(x,ordered=T))
setMethod('as.factor',
signature(x='MYCLASS'),
function(x)as.factor.MYCLASS(x))
setMethod('factor',
signature(x='MYCLASS'),
# re-capitulate the signature for base::factor()
function (x , levels, labels = levels, exclude = NA,
ordered = is.ordered(x), nmax = NA) {
ARGS <- list(x=x)
if(!missing(levels))
args['levels'] <- levels
if(!missing(labels))
args['labels'] <- labels
if(!missing(exclude))
args['exclude'] <- exclude
if(!missing(ordered))
args['ordered'] <- ordered
if(!missing(nmax))
warning('unused argument `nmax` in factor.MYCLASS')
do.call(as.factor.MYCLASS,ARGS)
})
setGenerics <- function(){
[contents from the original .onLoad method]
}
.onAttach <- function(libname,pkgname)
cat('Call setGenerics() for increased compatibility with `factor`, `as.factor`, and `table`.\n')