Это исправленный ответ.
Объединения классов - это фанки - они эффективно вставляют класс в середину существующей иерархии, поэтому внезапно вещи, расширяющие list
, теперь расширяются listOrNULL
.
Вместо этого я бы создал небольшую иерархию классов, которая представляет собой «Дерево», которое может быть «Пустым» или «Внутренним». Класс «Внутренний» будет иметь слот для хранения данных (типа «ЛЮБОЙ»), а также левой и правой ссылок, которые будут элементами «Дерева».
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")))
Я напишу конструктор для моего дерева с методами создания пустого дерева и дерева из элемента плюс левого и правого потомков.
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)
})
Основная операция - вставить элемент x
в дерево 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 < t@elem) {
l <- insert(x, t@left)
r <- t@right
} else {
l <- t@left
r <- insert(x, t@right)
}
Tree(t@elem, l, r)
})
Еще одна операция - проверка на членство
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 < t@elem) member(x, t@left)
else if (t@elem < x) member(x, t@right)
else TRUE
})
Интересным, функциональным свойством этой реализации является постоянство
> 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
Это не будет эффективным, когда имеется много обновлений, из-за неэффективности создания класса S4 и потому, что изменение дерева (например, добавление узла) копирует все узлы на пути к новому узлу. другой подход представляет дерево в виде matrix
левой, правой и тройной значений. Реализация S4 все еще будет иметь низкую производительность, потому что обновления экземпляра будут создавать новые экземпляры, дублируя все. Таким образом, я бы оказался в ссылочном классе с полями 'value' (вектором того, что дерево должно содержать, и matrix
левых и правых отношений.