2013-08-12 12 views
5

Mam klasę podstawową (nazwijmy ją "A"), której reprezentacja jest wspólna dla wielu innych klas.Jak zdefiniować prototyp S4 dla dziedziczonych gniazd?

Dlatego też definiuję inne klasy, takie jak "B", aby zawrzeć tę klasę.

Chciałbym ustawić prototyp tych innych klas (B) zawierają wartości domyślne dla szczelin odziedziczonych A. Myślałem, że to byłoby naturalne:

setClass("A", representation(a="character")) 
setClass("B", contains="A", prototype(a = "hello")) 

Ale to daje błąd:

Error in representation[!slots] : object of type 'S4' is not subsettable 

Nie wiem, dlaczego tak się dzieje. Jeśli pominąć prototyp mogę zrobić:

setClass("B", contains="A") 

a następnie siekać własną funkcję generatora:

new_B <- function(...){ 
      obj <- new("B", ...) 
      [email protected] = "hello" 
      obj 
     } 

a następnie utworzyć mojego obiektu na podstawie prototypu z new_B(), ale to jest strasznie surowy i brzydkie w porównaniu do użycia ogólnej generator new("B") i uwzględniając mój prototyp ...

Odpowiedz

2

wystarczy nazwać argument:

setClass("A", representation(a="character")) 
setClass("B", contains="A", prototype=prototype(a="hello")) 
+2

Również, a właściwie nie część pytania, ".A <- setClass (" A ", reprezentacja (a =" znak "))' pozwala '.A()' lub '.A (a =" foo ")' jako konstruktorów, zamiast 'nowych (" A ")'. Używam '.A <- ...' zamiast 'A <- ...', ponieważ nadal uważam, że często będzie pisać bardziej formalny konstruktor o nazwanych argumentach, itp., Który wywołuje '.A()' jako jego ostateczny linia. –

+0

@MartinMorgan Dzięki, niezła sztuczka! Przydałoby się zobaczyć przykład twojego bardziej formalnego konstruktora za pomocą '.A()', nie wiesz dlaczego wolisz to od 'nowego (" A ")' ?. – cboettig

5

Uzupełnienie mój komentarz raczej zapewniając nową odpowiedź na pytanie, oto rozwiązanie, gdzie nadal pasuje argumenty o położeniu (bo określić dodatkowe reprezentację klasy B):

.A <- setClass("A", representation(a="character")) 
.B <- setClass("B", representation(b="numeric"), 
    prototype(a="hello"), 
    contains="A") 

.A() i .B() zastąpić wywołań new("A") i new("B"). Na pewnym poziomie jest to cukier syntaktyczny, ale może sprawić, budowa obiektu bardziej przejrzysty

## construct an object using B's prototype, like new("B", b=1:3) 
> .B(b=1:3) 
An object of class "B" 
Slot "b": 
[1] 1 2 3 

Slot "a": 
[1] "hello" 

## construct an object using A's prototype, like new("B", new("A"), b=1:3) 
> .B(.A(), b=1:3) 
An object of class "B" 
Slot "b": 
[1] 1 2 3 

Slot "a": 
character(0) 

(drugi przykład wykorzystuje fakt, że nienazwane argumenty new lub B są używane do inicjalizacji klas dziedziczonych).

To nie jest tak przyjazny dla użytkownika, aby użyć .A lub .B bezpośrednio, na przykład dlatego, że podpis jest tylko ... i tak byłyby udokumentowane jako „patrz definicja szczelinami do klasy A”. To zakłóca rozdział interfejsu i implementacji, który jest siłą OOP. Również jedno lub drugie zachowanie w ostatniej części kodu (.B(.A(a=a), b=b) lub .B(a=a, b=b)) może nie być zamiarem. Więc zamiast zapewniać funkcję jest narażone na użytkownika, może jakiejś wstępnej masaż danych

A <- function(a=character(), ...) { 
    ## nothing special, just a public constructor 
    .A(a=a, ...) 
} 

B <- function(b, a="hello", ...) { 
    a <- tolower(a) ## no SHOUTing! 
    .B(A(a=a), b=b) ## A's public interface; no need for B to know A's details 
} 

Funkcje A i B zdefiniować interfejs, zapewnić użytkownikowi wskazówki o tym, co dopuszczalne argumenty są bez związania konstruktora do definicji klasy i wykonać wstępne masowanie danych. Te ostatnie mogą sprawić, że metody initialize będą zbędne, co jest dobre, ponieważ mają one complicated contract (powinny one być inicjatorami kopiowania i i jak widzieliśmy powyżej nienazwane argumenty mają inicjować klasy bazowe), które najbardziej ludzie się mylą.

Przeważnie są to tylko moje opinie.

Powiązane problemy