當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


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。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。