【问题标题】:Method dispatch when mixing S3 and S4混合 S3 和 S4 时的方法调度
【发布时间】:2020-02-29 20:05:10
【问题描述】:

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

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 类对象。

看起来,尽管调用了 S3 泛型,但 S3 调度系统被完全忽略了。创建函数print.lsmobj &lt;- function(obj) print('S3')(因为class(l)lsmobj)并运行cld(l) 不会打印"S3"

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

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

然而,我想知道如何实现cld.ref.grid 最终将被调用而没有像debugonce 这样的任何“技巧”。也就是说,R 执行了哪些步骤才能到达cld.ref.grid

【问题讨论】:

  • 你真的应该升级 lsmeans。你有一个非常旧的版本。 lsmobj 类很久以前就消失了。
  • 啊,我知道这一点。这个问题并没有真正与包相关,而是与 R 的方法调度系统相关。只是我不得不调试一些仍在使用 lsmeans 的代码,因此将其用作示例。
  • 我想知道您是否先加载 multcomp 库,如果 cld 会出现在 showMethods 中。也就是说,如果泛型不可见,为什么要显示方法。
  • OP 说 '... cld(l) doesn't print "S3" ' 但我认为这是一个错字,因为您定义了 print 方法,而不是 cld 方法。
  • @rvl,你是对的。没想到。

标签: r s4 r-s3


【解决方案1】:

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

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

2) 如果函数是非标准 S4 泛型,则执行其主体,然后在某些时候调用 S4 调度本身。

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

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

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

“通过调用UseMethod 来分派S3 方法的函数是普通函数,而不是来自"genericFunction" 类的对象。它们与任何其他函数一样是通用的,但需要一些特殊注意事项来确保S4 和S3 方法分派是一致(参见 Methods_for_S3)。”

【讨论】:

    【解决方案2】:

    为了注册 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 在执行 multcomp::cld(l) 时如何确定调用函数 cld.ref.grid
    • 因为multcomp::cld调用UseMethod,后者又为l寻找可能的方法,它属于c("lsmobj", "ref.grid");并发现确实有一个注册了ref.grid 类,但没有一个注册lsmobj。所以它使用ref.grid 的那个。方法注册在 lsmeansNAMESPACE 文件中完成,其中包括行 S3method("cld", "ref.grid")
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-29
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2015-03-05
    • 2018-04-28
    相关资源
    最近更新 更多