当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


R NextMethod 调用继承的方法


R语言 NextMethod 位于 methods 包(package)。

说明

callNextMethod 的调用只能出现在方法定义中。然后,它会调用当前方法之后的第一个继承方法,并将当前方法的参数传递给下一个方法。该方法调用的值是 callNextMethod 的值。

用法

callNextMethod(...)

参数

...

可选地,函数在下一次调用中的参数(但请注意,调度如下面的详细说明所示;参数对选择下一个方法没有影响。)

如果对 callNextMethod 的调用中不包含任何参数,则效果是使用当前参数调用该方法。请参阅详细说明以了解这的真正含义。

不带参数调用通常是使用 callNextMethod 的自然方式;请参阅示例。

细节

‘next’ 方法(即第一个继承方法)被定义为如果当前方法不存在则将被调用的方法。这就是 more-or-less 所发生的情况:当前方法(准确地说,是由调用 callNextMethod 的方法的 defined 槽给出的签名的方法)从当前方法的副本中删除通用,并且调用 selectMethod 来查找下一个方法(结果缓存在发生调用的方法对象中,因此每个参数类组合的每个会话通常只进行一次搜索)。

下一个方法是根据当前方法的签名定义的,而不是根据参数的实际类定义的。特别是,修改任何参数都不会影响选择。因此,如果调用函数在 callNextMethod() 调用之前分配不同类的对象,则可以使用无效参数调用所选的下一个方法。请小心对此类参数的任何分配。

即使原始方法集是一致的,下一个方法的选择也可能不明确。请参阅“Ambiguous Selection” 部分。

使用当前参数调用该方法的声明更准确如下。当前调用中丢失的参数仍然丢失(请记住,"missing" 是方法签名中的有效类)。对于原始调用中出现的形式参数,例如 x ,在下一个方法调用中会有一个对应的参数,相当于 x = x 。实际上,这意味着下一个方法会看到相同的实际参数,但参数仅计算一次。

所选方法返回的值。

模棱两可的选择

有两种相当常见的情况,其中下一个方法的选择是不明确的,即使原始方法集明确地唯一定义了所有方法选择。在这些情况下,应该通过调用特定函数或调用具有不同参数的泛型来替换callNextMethod()

最有可能出现的情况是二元运算符的方法,通常是通过一组通用函数之一。请参阅下面的"rnum" 类示例。此类示例通常需要三个方法:两个用于第一个或第二个参数来自类的情况,第三个用于两个参数都来自类的情况。如果最后一个方法使用 callNextMethod ,则其他两个方法同样有效。这种歧义性与首先需要定义双参数方法的歧义性完全相同。

事实上,这两种可能性在概念上和形式上都同样有效。如下例所示,应用程序的逻辑通常需要显式选择计算,或者使用修改后的参数调用通用函数来选择适当的方法。

歧义的另一个可能来源是直接从多个其他类继承的类(标准术语中的“mixin”)。如果泛型具有与两个超类对应的方法,则再次需要当前类的方法来解决歧义。使用callNextMethod将再次重新产生歧义。同样,必须在调用方法中做出一些明确的选择。

这些歧义并不是糟糕设计的结果,但它们确实需要解决方法。其他歧义通常反映继承树中的不一致,例如某个类出现在超类中的多个位置。这种情况应该很少见,但是由于多个包中的类独立定义,所以不能排除这种情况。

例子

## callNextMethod() used for the Math, Math2 group generic functions

## A class to automatically round numeric results to "d" digits

rnum <- setClass("rnum", slots = c(d = "integer"), contains = "numeric")

## Math functions operate on the rounded numbers, return a plain
## vector.  The next method will always be the default, usually a primitive.
setMethod("Math", "rnum",
          function(x)
              callNextMethod(round(as.numeric(x), x@d)))
setMethod("Math2", "rnum",
          function(x, digits)
              callNextMethod(round(as.numeric(x), x@d), digits))

## Examples of callNextMethod with two arguments in the signature.

## For arithmetic and one rnum with anything, callNextMethod with no arguments
## round the full accuracy result, and return as plain vector
setMethod("Arith", c(e1 ="rnum"),
          function(e1, e2)
              as.numeric(round(callNextMethod(), e1@d)))
setMethod("Arith", c(e2 ="rnum"),
          function(e1, e2)
              as.numeric(round(callNextMethod(), e2@d)))

## A method for BOTH arguments from "rnum" would be ambiguous
## for callNextMethod(): the two methods above are equally valid.
## The method chooses the smaller number of digits,
## and then calls the generic function, postponing the method selection
## until it's not ambiguous.
setMethod("Arith", c(e1 ="rnum", e2 = "rnum"),
          function(e1, e2) {
              if(e1@d <= e2@d)
                  callGeneric(e1, as.numeric(e2))
              else
                  callGeneric(as.numeric(e1), e2)
          })

## For comparisons, callNextMethod with the rounded arguments
setMethod("Compare", c(e1 = "rnum"),
          function(e1, e2)
              callNextMethod(round(e1, e1@d), round(e2, e1@d)))
setMethod("Compare", c(e2 = "rnum"),
          function(e1, e2)
              callNextMethod(round(e1, e2@d), round(e2, e2@d)))

## similarly to the Arith case, the method for two "rnum" objects
## can not unambiguously use callNextMethod().  Instead, we rely on
## The rnum() method inhertited from Math2 to return plain vectors.
setMethod("Compare", c(e1 ="rnum", e2 = "rnum"),
          function(e1, e2) {
              d <- min(e1@d, e2@d)
              callGeneric(round(e1, d), round(e2, d))
          })




set.seed(867)

x1 <- rnum(10*runif(5), d=1L)
x2 <- rnum(10*runif(5), d=2L)

x1+1
x2*2
x1-x2

## Simple examples to illustrate callNextMethod with and without arguments
B0 <- setClass("B0", slots = c(s0 = "numeric"))

## and a function to illustrate callNextMethod

f <- function(x, text = "default") {
    str(x) # print a summary
    paste(text, ":", class(x))
}

setGeneric("f")
setMethod("f", "B0", function(x, text = "B0") {
    cat("B0 method called with s0 =", x@s0, "\n")
    callNextMethod()
})

b0 <- B0(s0 = 1)

## call f() with 2 arguments: callNextMethod passes both to the default method
f(b0, "first test")

## call f() with 1 argument:  the default "B0" is not passed by callNextMethod
f(b0)

## Now, a class that extends B0, with no methods for f()
B1 <- setClass("B1", slots = c(s1 = "character"), contains = "B0")
b1 <- B1(s0 = 2, s1 = "Testing B1")

## the two cases work as before, by inheriting the "B0" method

f(b1, b1@s1)

f(b1)

B2 <- setClass("B2", contains = "B1")

## And, a method for "B2" that calls with explicit arguments.
## Note that the method selection in callNextMethod
## uses the class of the *argument* to consistently select the "B0" method

setMethod("f", "B2", function(x, text = "B1 method") {
    y <- B1(s0 = -x@s0, s1 ="Modified x")
    callNextMethod(y, text)
})

b2 <- B2(s1 = "Testing B2", s0 = 10)

f(b2, b2@s1)

f(b2)


## Be careful:  the argument passed must be legal for the method selected
## Although the argument here is numeric, it's still the "B0" method that's called
setMethod("f", "B2", function(x, text = "B1 method") {
    callNextMethod(x@s0, text)
})

##  Now the call will cause an error:

tryCatch(f(b2), error = function(e) cat(e$message,"\n"))




参考

Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 10.)

也可以看看

callGeneric 使用当前调度规则调用通用函数(通常用于组通用函数); Methods_Details 用于方法分派的一般行为。

相关用法


注:本文由纯净天空筛选整理自R-devel大神的英文原创作品 Call an Inherited Method。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。