questa è una risposta rivisto.
Le unioni di classe sono funky - inseriscono efficacemente una classe nel mezzo di una gerarchia esistente, quindi improvvisamente le cose che si estendono list
ora estendono listOrNULL
.
Invece mi piacerebbe creare una piccola gerarchia di classe che rappresenta un "albero" che può essere sia "vuoto" o "interno". La classe "Internal" avrà uno slot per contenere i dati (di tipo "ANY"), più i link sinistro e destro, che saranno elementi "Albero".
setClass("Tree")
setClass("Empty", contains="Tree")
setClass("Internal", contains="Tree",
representation=representation(elem="ANY", left="Tree", right="Tree"),
prototype=prototype(left=new("Empty"), right=new("Empty")))
scriverò un costruttore per il mio albero, con i metodi per la creazione di un albero vuoto, e un albero da un elemento più discendenti sinistro e destro.
setGeneric("Tree", function(elem, left, right) standardGeneric("Tree"),
signature="elem")
setMethod(Tree, "missing", function(elem, left, right) new("Empty"))
setMethod(Tree, "ANY", function(elem, left, right) {
new("Internal", elem=elem, left=left, right=right)
})
Un funzionamento di base è quella di inserire un elemento x
in un albero t
setGeneric("insert", function(x, t) standardGeneric("insert"))
setMethod(insert, c("ANY", "Empty"), function(x, t) {
Tree(x, Tree(), Tree())
})
setMethod(insert, c("ANY", "Internal"), function(x, t) {
if (x < [email protected]) {
l <- insert(x, [email protected])
r <- [email protected]
} else {
l <- [email protected]
r <- insert(x, [email protected])
}
Tree([email protected], l, r)
})
Un'altra operazione è quello di verificare l'appartenenza
setGeneric("member", function(x, t) standardGeneric("member"))
setMethod(member, c("ANY", "Empty"), function(x, t) FALSE)
setMethod(member, c("ANY", "Internal"), function(x, t) {
if (x < [email protected]) member(x, [email protected])
else if ([email protected] < x) member(x, [email protected])
else TRUE
})
Una interessante, funzionale, proprietà di questa implementazione è che è persistente
> t <- Tree()
> t1 <- insert(10, t)
> t2 <- insert(5, t1)
> t3 <- insert(7, t2)
> t4 <- insert(15, t3)
> which(sapply(1:20, member, t4))
[1] 5 7 10 15
> which(sapply(1:20, member, t2))
[1] 5 10
Questo non sta per essere efficace quando ci sono un sacco di aggiornamenti, a causa delle inefficienze della creazione di classe S4 e per la modifica di un albero (ad esempio, l'aggiunta di un nodo) copie tutti i nodi del percorso per il nuovo nodo. A different approach rappresenta l'albero come matrix
di triple a sinistra, a destra, valore. L'implementazione S4 avrebbe comunque prestazioni scarse, perché gli aggiornamenti dell'istanza creerebbero nuove istanze, duplicando tutto. Quindi finirò in una classe di riferimento, con il valore "campi" (un vettore di ciò che l'albero dovrebbe contenere e uno matrix
di relazioni sinistra e destra.
Questo è l'approccio consigliato da Chambers in Software per Data Analysis (+1) – Henrik
perfetto, grazie – RockScience
Funziona perfettamente, molte grazie – eblondel