2016-03-14 33 views
0

我已經創建了幾個S3方法,我通過在對象上設置類來進行控制。事情是這樣的:結合S3方法

myfun <- function (x) { 
    UseMethod("myfun") 
} 

myfun.a <- function(x) { 
    print("Type a") 
} 

myfun.b <- function(x) { 
    print("Type b") 
} 

myfun.c <- function(x) { 
    print("Type c") 
} 

myfun(structure(c(1:3), class = "a")) 
# [1] "Type a" 
myfun(structure(c(1:3), class = "b")) 
# [1] "Type b" 
myfun(structure(c(1:3), class = "c")) 
# [1] "Type c" 

有時候,這些類的某個子集能夠共享的方法是這樣的:

otherfun <- function (x) { 
    UseMethod("otherfun") 
} 

otherfun.a <- function(x) { 
    print("Type a") 
} 

otherfun.b <- function(x) { ## Doesn't work because its only called for "b" 
    print("Type b or c") 
} 

otherfun(structure(c(1:3), class = "a")) 
# [1] "Type a" 
otherfun(structure(c(1:3), class = "b")) 
# [1] "Type b or c" 
otherfun(structure(c(1:3), class = "c")) 
# [1] "Type b or c" 

什麼是實現多個類型共用一個最好的/最正確的方法方法?我不想在對象上堆疊類,因爲類「b」和「c」在大多數方面都不相同。

我正在考慮使用默認的方法和手動解析類的類型,是這樣的:

otherfun <- function (x) { 
UseMethod("otherfun") 
} 

otherfun.a <- function(x) { 
    print("Type a") 
} 

otherfun.b <- function(x) { 
    print("Type b or c") 
} 

otherfun.default <- function(x) { 
    if(class(x) == "c") otherfun.b(x) 
    else stop("Bad Class on x") 
} 

otherfun(structure(c(1:3), class = "a")) 
# [1] "Type a" 
otherfun(structure(c(1:3), class = "b")) 
# [1] "Type b or c" 
otherfun(structure(c(1:3), class = "c")) 
# [1] "Type b or c" 

回答

4

就分配在一起就像這樣:

​​
+0

我喜歡這一點,謝謝...明天我會接受它作爲答案。 –

4

定義一個適當的類hierarcy

a <- structure(list(), class=c("a", "base")) 
b <- structure(list(), class=c("b", "b_or_c", "base")) 
c <- structure(list(), class=c("c", "b_or_c", "base")) 

並在適當的地方實施方法

im <- function(x) UseMethod("im") 
im.a <- function(x) "I'm a" 
im.b_or_c <- function(x) paste("I'm b_or_c:", class(x)[1]) 

在動作:因爲共享結構的

> im(a) 
[1] "I'm a" 
> im(b) 
[1] "I'm b_or_c: b" 
> im(c) 
[1] "I'm b_or_c: c" 

一般類份額的方法。定義適當的層次結構允許重用方法和數據結構。

同一類的對象應始終具有相同的類層次結構,因此'b'將始終爲'b_or_c'。這在許多對象系統中被強制執行; S3允許你用類定義玩得非常快速和鬆散。執行一個共同的層次結構的一種方法是使用「建設者」,集中在類

base = function(base_data="base only", ..., class) 
    ## construct an instance that contains base data, other data (...), 
    ## and an appropriate class specification 
    structure(list(base_data=base_data, ...), class=c(class, "base")) 

b_or_c = function(b_or_c_data="b or c", ..., class) 
    ## construct a b_or_c instance with relevant data, using the 
    ## base constructor 
    base(b_or_c_data=b_or_c_data, ..., class=c(class, "b_or_c")) 

b = function(b_data="b only", ...) 
    ## like b_or_c, but for a 'b' instance 
    b_or_c(b_data=b_data, ..., class="b") 

這動作看起來像

> b(b_data="ima b") 
$base_data 
[1] "base only" 

$b_or_c_data 
[1] "b or c" 

$b_data 
[1] "ima b" 

attr(,"class") 
[1] "b"  "b_or_c" "base" 

即使有上b_or_c沒有方法的創建,它可能讓班級代表b和c班級之間共享的數據仍然很有用。如果沒有,那麼刪除b_or_c構造函數並調整b()以調用base()

這可能是abc共享方法,在這種情況下該方法應該在基類上實現。當ab共享方法,但不共享c時,通常會編寫a和b方法在初始處理後調用的幫助程序函數;通常這種圖案(其中一個方法是在對象實現從僅在層次結構的不同部分某些類)的建議的類層次結構沒有適當地設計

.foo_helper = function(x) { 
    ## implement shared functionality here 
} 

foo.a = function(x) { 
    ## do a-specfic things here, then 
    data = some_manipulation_of_a 
    .foo_helper(data) 
} 

foo.b = function(x) { 
    data = some_manipulation_of_b 
    .foo_helper(data) 
} 

可能的,這將是比一個更逼真的圖案接受的答案 - ac在調用某些共享功能之前可能需要單獨處理(因爲它們具有不同的數據結構)。

+0

感謝您提醒我將基礎班級保留在班級列表中。不過,我會接受@ g-grothendieck的回答。 您的風格看起來更難維護和使用。例如,我是否將所有「b」和「c」對象標記爲「b_or_c」,或者只有那些我期望需要混合方法的對象? 如果所有的混合方法都被刪除而偏向獨特的方法會怎樣? 如果我認識到「a」可以在某個點與「c」共享某個方法,該怎麼辦? –

+0

我在回覆中詳細闡述了一下。 –