Methods_for_Nongenerics
位於 methods
包(package)。 說明
在編寫方法時R包中,這些方法通常應用於該包中不通用的函數(在另一個包中);也就是說,該函數在其自己的包中沒有正式的方法,盡管它可能有 S3 方法。在這種情況下的編程涉及一個額外的步驟,調用setGeneric()
聲明該函數是在你的包中是通用的。
然後,對包中函數的調用將使用其中定義的所有方法或創建相同通用函數的任何其他加載包中定義的方法。同樣,對這些包中的函數的調用將使用您的方法。
然而,原始版本仍然是非通用的。除特殊情況外,該包或使用該版本的其他包中的調用不會調度您的方法:
-
如果該函數是接受方法的原始函數之一,並且參數之一是 S4 對象(應該是這種情況),則內部 C 實現將調度方法。
-
如果該函數的其他版本調度 S3 方法,並且您的方法也注冊為 S3 方法,則該方法通常會作為該 S3 方法調度。
-
否則,您將需要確保對該函數的所有調用都來自該函數是通用的包,也許可以通過將代碼複製到您的包中來實現。
以下各節將討論詳細信息和根本原因。
通用和非通用調用
為包中的函數(任何函數)創建方法意味著調用該包中的函數將根據實際參數選擇方法。但是,如果該函數最初是另一個包中的非泛型函數,則從該包調用該函數將不會調度方法。此外,來自導入非通用版本的任何第三個包的調用也不會調度方法。本節討論原因以及如何處理後果。
原因很簡單R命名空間機製及其在評估函數調用中的作用。當需要在對某個包中的函數的調用中評估名稱(例如函數的名稱)時,評估器首先在調用的框架中查找,然後在包的命名空間中查找,然後在導入中查找那個包。
為包中的函數定義方法可確保對該包中的函數的調用將選擇這些方法,因為該函數的通用版本是在命名空間中創建的。同樣,來自另一個具有或導入通用版本的包的調用將選擇方法。由於通用版本相同,因此所有方法在所有這些包中都可用。
但是,來自任何導入舊版本或僅從搜索列表中選擇它的包的調用通常不會選擇方法。
舉個例子,考慮基礎包中的函數data.frame()
。該函數將任意數量的對象作為參數,並嘗試將它們作為變量組合到 DataFrame 對象中。它通過為每個對象調用同樣在基礎包中的 as.data.frame()
來實現此目的。
合理的目標是通過定義 as.data.frame()
的方法來擴展可包含在 DataFrame 中的對象類。但是對 data.frame()
的調用仍將使用基礎包中該函數的版本,該版本繼續調用該包中的非通用 as.data.frame()
。
發生的事情的細節以及處理它的選項取決於函數的形式:原始函數;調度 S3 方法的函數;或一個普通的R函數。
原始函數並不實際R函數對象。它們直接進入內部 C 代碼。然而,其中一些已被實現來識別方法。這些函數從內部 C 代碼調度 S4 和 S3 方法。 S3 或 S4 都沒有明確的通用函數。如果第一個參數或二元運算符的任一參數是 S4 對象,則內部代碼將查找 S4 方法。如果沒有找到S4方法,則搜索S3方法。因此,隻要定義了相關的類,為這些函數定義方法就可以工作,情況應該始終如此。
函數通過調用 UseMethod()
調度 S3 方法,無論第一個參數是否是 S4 對象,它都不會查找形式方法。這適用於上麵的 as.data.frame()
示例。要在這種情況下調用方法,如果可能的話,您的包還必須將該方法定義為 S3 方法。請參閱“S3 “Generic” 函數”部分。
在第三種可能性中,函數的定義不需要任何方法。例如,基礎包具有許多計算矩陣參數的數值分解的函數。有些,例如 chol()
和 qr()
是為了調度 S3 方法而實現的;其他的,例如svd()
,直接作為特定計算來實現。可以直接編寫和調用後一個函數的通用版本來定義形式方法,但不導入此通用版本的另一個包中的代碼不會調度此類方法。
在這種情況下,您需要在對提供應分派方法的參數的函數的所有間接調用中使用通用版本。這可能需要提供新函數來分派方法,然後調用它們替換的函數。例如,如果 S3 方法不適用於 as.data.frame()
,則可以調用一個函數,該函數將通用版本應用於其所有參數,然後調用 data.frame()
作為該函數的替換。如果所有其他方法都失敗,則可能需要複製相關函數,以便他們找到通用版本。
S3“Generic”函數
S3 方法調度查看第一個參數的類。 S3 方法是普通函數,具有與通用函數相同的參數。 S3 方法的 “signature” 由該方法分配的名稱來標識,該名稱由通用函數的名稱、"."
和類的名稱組成。有關詳細信息,請參閱UseMethod
。
要實現與 S4 類對應的這些函數之一的方法,有兩種可能性:S4 方法或具有 S4 類名稱的 S3 方法。僅當預期簽名具有第一個參數且沒有其他參數時,S3 方法才可行。在這種情況下,推薦的方法是定義 S3 方法,並提供與 S4 方法的定義相同的函數。如果 S3 通用函數是 f3(x, ...)
並且新方法的 S4 類是 "myClass"
:
f3.myClass <- function(x, ...) { ..... }
setMethod("f3", "myClass", f3.myClass)
定義這兩種方法通常可以確保對原始函數的所有調用都將調度預期的方法。使用該函數的原始版本不會從其他包中單獨調用 S4 方法。另一方麵,如果存在任何符合條件的非默認 S4 方法,則不會單獨調用 S3 方法。
S4和S3方法選擇被設計為盡可能遵循兼容的繼承規則。 S3 類可用於任何 S4 方法選擇,前提是 S3 類已通過調用 setOldClass
注冊,並且該調用指定了正確的 S3 繼承模式。 S4類可用於任何S3方法選擇;當檢測到 S4 對象時,S3 方法選擇使用 extends(class(x))
的內容作為 S3 繼承的等效內容(繼承在第一次調用後被緩存)。
現有的 S3 方法可能無法按照 S4 子類所需的方式運行,在這種情況下,asS3
和 S3Part
等實用程序可能很有用。如果S3方法在S4對象上失敗,則可以傳遞asS3(x)
;如果 S3 方法返回的對象需要合並到 S4 對象中,S3Part
的替換函數可能會很有用。
例子
## A class that extends a registered S3 class inherits that class' S3
## methods.
setClass("myFrame", contains = "data.frame",
slots = c(timestamps = "POSIXt"))
df1 <- data.frame(x = 1:10, y = rnorm(10), z = sample(letters,10))
mydf1 <- new("myFrame", df1, timestamps = Sys.time())
## "myFrame" objects inherit "data.frame" S3 methods; e.g., for `[`
## IGNORE_RDIFF_BEGIN
mydf1[1:2, ] # a data frame object (with extra attributes)
## IGNORE_RDIFF_END
## a method explicitly for "myFrame" class
setMethod("[",
signature(x = "myFrame"),
function (x, i, j, ..., drop = TRUE)
{
S3Part(x) <- callNextMethod()
x@timestamps <- c(Sys.time(), as.POSIXct(x@timestamps))
x
}
)
## IGNORE_RDIFF_BEGIN
mydf1[1:2, ]
## IGNORE_RDIFF_END
setClass("myDateTime", contains = "POSIXt")
now <- Sys.time() # class(now) is c("POSIXct", "POSIXt")
nowLt <- as.POSIXlt(now)# class(nowLt) is c("POSIXlt", "POSIXt")
mCt <- new("myDateTime", now)
mLt <- new("myDateTime", nowLt)
## S3 methods for an S4 object will be selected using S4 inheritance
## Objects mCt and mLt have different S3Class() values, but this is
## not used.
f3 <- function(x)UseMethod("f3") # an S3 generic to illustrate inheritance
f3.POSIXct <- function(x) "The POSIXct result"
f3.POSIXlt <- function(x) "The POSIXlt result"
f3.POSIXt <- function(x) "The POSIXt result"
stopifnot(identical(f3(mCt), f3.POSIXt(mCt)))
stopifnot(identical(f3(mLt), f3.POSIXt(mLt)))
## An S4 object selects S3 methods according to its S4 "inheritance"
setClass("classA", contains = "numeric",
slots = c(realData = "numeric"))
Math.classA <- function(x) { (getFunction(.Generic))(x@realData) }
setMethod("Math", "classA", Math.classA)
x <- new("classA", log(1:10), realData = 1:10)
stopifnot(identical(abs(x), 1:10))
setClass("classB", contains = "classA")
y <- new("classB", x)
stopifnot(identical(abs(y), abs(x))) # (version 2.9.0 or earlier fails here)
## an S3 generic: just for demonstration purposes
f3 <- function(x, ...) UseMethod("f3")
f3.default <- function(x, ...) "Default f3"
## S3 method (only) for classA
f3.classA <- function(x, ...) "Class classA for f3"
## S3 and S4 method for numeric
f3.numeric <- function(x, ...) "Class numeric for f3"
setMethod("f3", "numeric", f3.numeric)
## The S3 method for classA and the closest inherited S3 method for classB
## are not found.
f3(x); f3(y) # both choose "numeric" method
## to obtain the natural inheritance, set identical S3 and S4 methods
setMethod("f3", "classA", f3.classA)
f3(x); f3(y) # now both choose "classA" method
## Need to define an S3 as well as S4 method to use on an S3 object
## or if called from a package without the S4 generic
MathFun <- function(x) { # a smarter "data.frame" method for Math group
for (i in seq_len(ncol(x))[sapply(x, is.numeric)])
x[, i] <- (getFunction(.Generic))(x[, i])
x
}
setMethod("Math", "data.frame", MathFun)
## S4 method works for an S4 class containing data.frame,
## but not for data.frame objects (not S4 objects)
try(logIris <- log(iris)) #gets an error from the old method
## Define an S3 method with the same computation
Math.data.frame <- MathFun
logIris <- log(iris)
參考
Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 10.)
也可以看看
Methods_for_S3 用於建議實現適用於 S3 和 S4 調度的方法。
相關用法
- R MethodsList 方法列表對象
- R MethodSupport 方法的附加(支持)函數
- R as 強製對象屬於某個類
- R language-class 表示未評估語言對象的類
- R className 類名包含對應的包
- R BasicClasses 基本數據類型對應的類
- R callGeneric 從方法調用當前通用函數
- R findClass 查找類定義
- R setOldClass 注冊舊式 (S3) 類和繼承
- R ReferenceClasses 具有按引用處理的字段的對象(OOP 樣式)
- R setGroupGeneric 創建函數的組通用版本
- R StructureClasses 基本結構對應的類
- R showMethods 顯示指定函數或類的所有方法
- R getMethod 獲取或測試方法的定義
- R slot 正式類對象中的槽
- R S4groupGeneric S4組通用函數
- R methodUtilities 用於方法和 S-Plus 兼容性的實用函數
- R getClass 獲取類定義
- R evalSource 使用源文件中的函數定義,無需重新安裝包
- R is 對象是來自類嗎?
- R isSealedMethod 檢查密封方法或類
- R cbind2 按列或行組合兩個對象
- R GenericFunctions 管理通用函數的工具
- R dotsMethods 在方法簽名中使用...
- R S3Part 包含 S3 類的 S4 類
注:本文由純淨天空篩選整理自R-devel大神的英文原創作品 Methods for Non-Generic Functions in Other Packages。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。