sistemi di classe
Scopri i tre sistemi di classe in R, S3, S4 e Referen classi
## S3 methods, Section 5 of
RShowDoc("R-lang")
## S4 classes
?Classes
?Methods
## Reference classes
?ReferenceClasses
Con un background di Java sarete tentati di andare con le classi di riferimento, ma questi hanno 'semantica di riferimento' e azione a distanza (cambiando un oggetto cambia un altro che si riferisce agli stessi dati), mentre la maggior parte Gli utenti R si aspettano una semantica 'copia su cambiamento'. Si possono fare grandi progressi con le classi S3, ma un approccio più disciplinato sarebbe a mio avviso adottare S4. Le caratteristiche di S4 ti sorprenderanno, in parte perché il sistema di classe è più vicino al comune sistema di oggetti Lisp che a Java.
Ci sono other opinions and options.
implementazione di base
Io non sono davvero sicuro di quello che il vostro obiettivo design con `ProcessData' è; Implementerei le tue due classi come una classe, un generico e un metodo per il generico che opera sulla classe MyClass.
## definition and 'low-level' constructor
.MyClass <- setClass("MyClass", representation(word="character"))
## definition of a generic
setGeneric("processData", function(x, ...) standardGeneric("processData"))
setMethod("processData", "MyClass", function(x, ...) {
cat("processData(MyClass) =", [email protected], "\n")
})
Questo è completo e funzionante
> myClass <- .MyClass(word="hello world")
> processData(myClass)
processData(MyClass) = hello world
Le tre linee di codice possono essere collocati in due file, "AllGenerics.R" e "MyClass.R" (compreso il metodo) o tre file "AllGenerics.R", "AllClasses.R", "processData-methods.R" (si noti che i metodi sono associati ai generici e vengono inviati alla classe).
Ulteriori implementazione
Uno normalmente aggiungere un costruttore più user-friendly, per esempio, fornendo suggerimenti per l'utente sui tipi di dati attesi o l'esecuzione di inizializzazione argomento complesso passi
MyClass <- function(word=character(), ...)
{
.MyClass(word=word, ...)
}
Tipicamente si vuole uno slot accessor, piuttosto che l'accesso diretto allo slot. Questa può essere una semplice funzione (come illustrato) o un metodo generico +.
word <- function(x, ...) [email protected]
Se lo slot deve essere aggiornato, uno scrive una funzione o un metodo di sostituzione. La funzione o il metodo di solito ha tre argomenti, l'oggetto da aggiornare, possibili argomenti aggiuntivi e il valore con cui aggiornare l'oggetto. Ecco un'implementazione metodo generico +
setGeneric("word<-", function(x, ..., value) standardGeneric("word<-"))
setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) {
## note double dispatch on x=MyClass, value=character
[email protected] <- value
x
})
Un po 'difficile implementazione alternativa è
setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) {
initialize(x, word=value)
})
che utilizza il initialize
metodo generico e di default come un costruttore di copia; questo può essere efficiente se si aggiornano più slot contemporaneamente.
Poiché la classe viene visto dagli utenti, si vuole visualizzare in modo intuitivo utilizzando un metodo 'show', per il quale un generico (getGeneric("show")
) esiste già
setMethod("show", "MyClass", function(object) {
cat("class:", class(object), "\n")
cat("word:", word(object), "\n")
})
Ed ora il nostro utente sessione assomiglia
> myClass
class: MyClass
word: hello world
> word(myClass)
[1] "hello world"
> word(myClass) <- "goodbye world"
> processData(myClass)
processData(MyClass) = goodbye world
efficienza
R funziona in modo efficiente su vettori; Le classi S4 non fanno eccezione. Quindi il design è che ogni slot di una classe rappresenta una colonna che si estende su più righe, piuttosto che l'elemento di una singola riga. Ci aspettiamo che la "parola" dello slot in genere contenga un vettore di lunghezza molto maggiore di 1 e che le operazioni agiscano su tutti gli elementi del vettore. Quindi, si potrebbe scrivere metodi con questo in mente, ad esempio, la modifica del metodo show per
setMethod("show", "MyClass", function(object) {
cat("class:", class(object), "\n")
cat("word() length:", length(word(object)), "\n")
})
Qui ci sono oggetti più grandi di dati (utilizzando i file sul mio sistema Linux)
> amer <- MyClass(readLines("/usr/share/dict/american-english"))
> brit <- MyClass(readLines("/usr/share/dict/british-english"))
> amer
class: MyClass
word() length: 99171
> brit
class: MyClass
word() length: 99156
> sum(word(amer) %in% word(brit))
[1] 97423
> amer_uc <- amer ## no copy, but marked to be copied if either changed
> word(amer_uc) <- toupper(word(amer_uc)) ## two distinct objects
e tutto questo è abbastanza performante.
pericoli della classe di riferimento 'action-at-a-distance' rewind
Let ad una più semplice implementazione della classe S4, con accesso diretto e slot per non costruttori di fantasia. Ecco il dizionario e una copia, trasformata in maiuscolo
.MyClass <- setClass("MyClass", representation(word="character"))
amer <- .MyClass(word=readLines("/usr/share/dict/american-english"))
amer_uc <- amer
[email protected] <- toupper([email protected])
Nota che abbiamo in maiuscolo amer_uc
ma non amer
:
> [email protected][99 + 1:10]
[1] "Adana" "Adar" "Adar's" "Addams" "Adderley"
[6] "Adderley's" "Addie" "Addie's" "Addison" "Adela"
> [email protected][99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
Questo è veramente ciò che gli utenti si aspettano R - I ho creato un oggetto separato e lo ho modificato; l'oggetto originale non è stato modificato. Questa è un'affermazione da parte mia; forse non so cosa si aspettano gli utenti R.Suppongo che un utente R non presti realmente attenzione al fatto che questa è una classe di riferimento, ma pensa che sia solo un altro oggetto R come un vettore integer()
o un data.frame
o il valore restituito di lm()
.
Al contrario, ecco una minima implementazione di una classe di riferimento, e operazioni simili
.MyRefClass <- setRefClass("MyRefClass", fields = list(word="character"))
amer <- .MyRefClass(word=readLines("/usr/share/dict/american-english"))
amer_uc <- amer
amer_uc$word <- toupper(amer_uc$word)
Ma ora abbiamo cambiato sia amer
e amer_uc
! Completamente atteso dai programmatori C o Java, ma non dagli utenti R.
> amer$word[99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
> amer_uc$word[99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
Quali odi * questo * in "è possibile?" fare riferimento a? È certamente possibile inserire diverse parti del programma in file diversi se questa è la domanda. Le classi di riferimento funzionano secondo le linee del tuo pseudo codice se questa è la domanda. Vedi '? SetRefClass'. –
Si noti che uno stile di programmazione orientato agli oggetti non è ciò che è più naturale per R, che ha più di uno stile funzionale di programmazione. –
La programmazione OO è così innaturale per R che è stata implementata circa quattro o cinque volte, mentre lo stile funzionale della programmazione è stato implementato una sola volta, con il linguaggio S stesso, e da allora non è cambiato molto. – Spacedman