2014-09-15 14 views
5

Diciamo che voglio definire due classi di classi, Sentence e Word. Ogni oggetto parola ha una stringa di caratteri e una parte di discorso (pos). Ogni frase contiene un numero di parole e ha uno slot aggiuntivo per i dati.Come posso far riempire uno slot di più oggetti dello stesso tipo in R?

La classe Word è semplice da definire.

wordSlots <- list(word = "character", pos = "character") 
wordProto <- list(word = "", pos = "") 
setClass("Word", slots = wordSlots, prototype = wordProto)  
Word <- function(word, pos) new("Word", word=word, pos=pos) 

Ora voglio fare una classe Sentence che può contenere alcuni Word s ed alcuni dati numerici.

se definisco la classe Sentence come così:

sentenceSlots <- list(words = "Word", stats = "numeric") 
sentenceProto <- list(words = Word(), stats = 0) 
setClass("Sentence", slots = sentenceSlots, prototype = sentenceProto) 

Poi la frase può contenere solo una parola. Potrei ovviamente definirlo con molti slot, uno per ogni parola, ma poi sarà limitato in lunghezza.

Tuttavia, se io definisco la classe Sentence in questo modo:

sentenceSlots <- list(words = "list", stats = "numeric") 
sentenceProto <- list(words = list(Word()), stats = 0) 
setClass("Sentence", slots = sentenceSlots, prototype = sentenceProto) 

può contenere quante più parole che voglio, ma lo slot words può contenere gli oggetti che non sono della classe Word.

C'è un modo per realizzare questo? Questo sarebbe simile alla cosa C++ in cui è possibile avere un vettore di oggetti dello stesso tipo.

+0

Penso che il mio precedente suggerimento (che ho cancellato), sia buono. In una frase, cambiala in un vettore di parole anziché in una lista di parole. Non faccio molta programmazione OO in R, ma penso che dovrebbe funzionare. – DMT

+0

Non lo interpreta come vettore, ma come lista. Con 'words =" vector "' e 'x <- new (" Frase ")', 'x @ words <- c (Word(), Word(), 3)' non causa errori e rende 'x @ words' una lista. –

+0

comprensibile vero? Perché hai due elementi di tipo Word e uno di tipo numerico? Sarà forzato prima che l'ambientazione abbia luogo. Il 3 corrisponde alle statistiche nell'oggetto frase? – DMT

risposta

6

Ricordando che R funziona bene sui vettori, un primo passo è quello di pensare a 'parole', piuttosto che 'parola'

## constructor, accessors, subset (also need [[, [<-, [[<- methods) 
.Words <- setClass("Words", 
    representation(words="character", parts="character")) 
words <- function(x) [email protected] 
parts <- function(x) [email protected] 
setMethod("length", "Words", function(x) length(words(x))) 
setMethod("[", c("Words", "ANY", "missing"), function(x, i, j, ...) { 
    initialize(x, words=words(x)[i], parts=parts(x)[i], ...) 
}) 

## validity 
setValidity("Words", function(object) { 
    if (length(words(object)) == length(parts(object))) 
     NULL 
    else 
     "'words()' and 'parts()' are not the same length" 
}) 

@ suggerimento di Nicola che uno ha una lista di parole è stata formalizzata nel IRanges pacchetto (in realtà, S4Vectors in "devel"/3.0 ramo di Bioconductor), dove un 'SimpleList' prende l'approccio 'ingenuo' di richiedere a tutti gli elementi della lista di avere la stessa classe, mentre un 'CompressedList' ha un comportamento simile ma in realtà è implementato come un oggetto vettoriale (uno con una lunghezza(), [, e [[metodi) che è 'partizionato' (o per la fine o la larghezza) in gruppi.

library(IRanges) 
.Sentences = setClass("Sentences", 
    contains="CompressedList",  
    prototype=c(elementType="Words")) 

Si potrebbe quindi scrivere un costruttore più user-friendly, ma le funzionalità di base è

## 0 Sentences 
.Sentences() 
## 1 sentence of 0 words 
.Sentences(unlistData=.Words(), partitioning=PartitioningByEnd(0)) 
## 3 sentences of 2, 0, and 3 words 
s3 <- .Sentences(unlistData=.Words(words=letters[1:5], parts=LETTERS[1:5]), 
    partitioning=PartitioningByEnd(c(2, 2, 5))) 

portando ad

> s3[[1]] 
An object of class "Words" 
Slot "word": 
[1] "a" "b" 

Slot "part": 
[1] "A" "B" 

> s3[[2]] 
An object of class "Words" 
Slot "word": 
character(0) 

Slot "part": 
character(0) 

> s3[[3]] 
An object of class "Words" 
Slot "word": 
[1] "c" "d" "e" 

Slot "part": 
[1] "C" "D" "E" 

noti che alcune operazioni tipiche sono veloci perché possono operare sugli elementi "non in elenco" senza creare o distruggere istanze S4, ad esempio, forzare tutte le "parole" in maiuscolo

setMethod(toupper, "Words", function(x) { [email protected] <- toupper([email protected]); x }) 
setMethod(toupper, "Sentences", function(x) relist(toupper(unlist(x)), x)) 

Questo è 'veloce' per ampie raccolte di frasi perché unlist/relist è davvero su un accesso di slot e la creazione di una singola istanza di 'Words'. Scalable Genomics with R and Bioconductor delinea questa e altre strategie.

In un @nicola risposta dice 'R non è perfettamente adatto per lo stile di programmazione OO', ma è probabilmente più utile per rendersi conto che oggetto S4 R stile orientato differisce da C++ e Java, così come R si differenzia da C. In particolare si tratta di davvero prezioso per continuare a pensare in termini di vettori quando si lavora con S4 - Parole piuttosto che Parola, Persone piuttosto che Persona ...

+0

Fa tutto quello che dovevo fare, e più di quanto mi aspettassi era facilmente possibile. Molto chiaro e informativo. Mi dà molte idee su come affrontare il mio problema! –

4

Suggerisco solo un work-around per questa classe di problemi. Tieni presente che R non è perfettamente adatto allo stile di programmazione OO e ogni soluzione difficilmente mostrerà la solidità di altri linguaggi come Java o C++. Tuttavia, è possibile dichiarare la propria classe Sentence con uno slot words come un elenco. Poi si definisce il costruttore come ad esempio:

Sentence<-function(words,stats) { 
    #check for the components' class of words argument 
    if (!is.list(words) || !all(sapply(words,function(x) class(x)=="Word"))) stop("Not valid words argument") 
    #create the object 
     new("Sentence", words=words, stats=stats) 
    } 

Un esempio di tale costruttore possono essere trovate nella confezione sp per la classe Polygons. Puoi vedere il corpo di quella funzione.

Se si vuole evitare che i set di utente in modo errato lo slot words, si può ridefinire il @<- operatore di tali come:

"@<-.Sentence"<-function(sentence,...) invisible(sentence) 

Non credo che l'ultimo passaggio è necessario. Qualunque cosa tu faccia, l'utente può sempre rovinare tutto. Ad esempio, potrebbe chiamare direttamente la funzione new ignorando il costruttore. Oppure può impostare la classe Word su un oggetto arbitrario e quindi passarla a Sentence. Come ho detto, R non è perfetto per questo stile di programmazione, quindi dovresti adottare spesso una soluzione non ottimale.