混合 S3 和 S4 时的方法调度

Method dispatch when mixing S3 and S4

我想了解 R 在混合 S3 和 S4 时为找到合适的函数所经历的步骤。这是一个例子:

set.seed(1)
d <- data.frame(a=rep(c('a', 'b'), each=15),
                b=rep(c('x', 'y', 'z'), times=5),
                y=rnorm(30))

m <- lme4::lmer(y ~ b + (1|a), data=d)
l <- lsmeans::lsmeans(m, 'b')
multcomp::cld(l)

我不完全理解执行最后一行时会发生什么。

multcomp::cld 打印 UseMethod("cld"),因此 S3 方法调度。

isS4(l) 表明 l 是 S4 class 对象。

似乎尽管调用了 S3 泛型,但 S3 调度系统被完全忽略了。创建函数 print.lsmobj <- function(obj) print('S3')(因为 class(l)lsmobj)和 运行 cld(l) 不打印 "S3".

showMethods(lsmobj)showMethods(ref.grid)(超级 class),不要列出任何类似于 cld 函数的内容。

使用debugonce(multcomp::cld)表明最终调用的函数是cld.ref.grid from lsmeans.

不过,我想知道如何实现 cld.ref.grid 最终会像 debugonce 一样在没有任何 "tricks" 的情况下被调用。也就是说,R 执行哪些步骤才能到达 cld.ref.grid.

为了注册 S3 方法,泛型必须可用。在这里,我为 merMod 个对象编写了一个简单的 foo 方法:

> library(lme4)
> foo.merMod = function(object, ...) { "foo" }

> showMethods(class = "merMod")

Function ".DollarNames":
 <not an S4 generic function>

Function "complete":
 <not an S4 generic function>

Function "formals<-":
 <not an S4 generic function>

Function "functions":
 <not an S4 generic function>
Function: getL (package lme4)
x="merMod"

Function "prompt":
 <not an S4 generic function>
Function: show (package methods)
object="merMod"

> methods(class = "merMod")
 [1] anova          as.function    coef           confint        cooks.distance
 [6] deviance       df.residual    drop1          extractAIC     family        
[11] fitted         fixef          formula        getL           getME         
[16] hatvalues      influence      isGLMM         isLMM          isNLMM        
[21] isREML         logLik         model.frame    model.matrix   ngrps         
[26] nobs           plot           predict        print          profile       
[31] ranef          refit          refitML        rePCA          residuals     
[36] rstudent       show           sigma          simulate       summary       
[41] terms          update         VarCorr        vcov           weights              

两个列表都不包含 foo。但是如果我们定义泛型,那么它会出现在 methods() 结果中:

> foo = function(object, ...) UseMethod("foo")
> methods(class = "merMod")
 [1] anova          as.function    coef           confint        cooks.distance
 [6] deviance       df.residual    drop1          extractAIC     family        
[11] fitted         fixef          foo            formula        getL          
[16] getME          hatvalues      influence      isGLMM         isLMM         
[21] isNLMM         isREML         logLik         model.frame    model.matrix  
[26] ngrps          nobs           plot           predict        print         
[31] profile        ranef          refit          refitML        rePCA         
[36] residuals      rstudent       show           sigma          simulate      
[41] summary        terms          update         VarCorr        vcov          
[46] weights       

现在包括foo

同样,在您的示例中,如果您执行 library(multcomp)methods() 将显示 cld 的存在,因为那是 cld 的泛型所在的位置。

较旧的 R 文档(2016 年之前)过去包含比当前文档更多的详细信息,但粗略地说,该过程按优先级降序排列如下:

1) 如果函数是标准的 S4 泛型并且签名中的任何参数都是 S4(根据 isS4),则根据通常的规则选择最佳的 S4 方法。

2) 如果函数是一个非标准的 S4 泛型,那么它的主体将被执行,然后在某个时候调用 S4 调度本身。

3) 如果函数是 S3 通用函数,则 S3 分派发生在第一个参数上(内部通用二元运算符除外)。

4) 如果该函数根本不是泛型的,那么它会以通常的方式对其所有参数进行惰性求值。

请注意,来自 setGeneric 的帮助页面:

"Functions that dispatch S3 methods by calling UseMethod are ordinary functions, not objects from the "genericFunction" class. They are made generic like any other function, but some special considerations apply to ensure that S4 and S3 method dispatch is consistent (see Methods_for_S3)."