#scheme #lisp #binary-tree #binary-search-tree
#схема #lisp #двоичное дерево #binary-search-tree
Вопрос:
Я реализовал функцию для вставки узлов в двоичное дерево.
Узел дерева представляет собой список формы (left-node key right-node)
.
(insert tree n)
это моя функция вставки узла, где дерево — это список узлов дерева в двоичном дереве поиска, и я хочу добавить ключевое значение n в качестве нового узла.
Итак, (insert '(((() 4 ()) 5 (() 2 ())) 6 ()) 7)
выдает вывод в виде (((() 4 ()) 5 (() 2 ())) 6 (() 7 ()))
.
Теперь я хочу сделать что-то вроде этого :
1. Определите список значений. (define (f) (list '1 `2 `3 `4 `5 ))
2. Выполните цикл по списку и введите значения одно за другим в дереве, начиная с пустого дерева.
Итак, когда я попытался выполнить шаг 2, я сделал следующее :
(define x ()) ;; Tree x is initially empty
(insert x 2) ;; This prints (() 2 ())
(insert x 1) ;; This prints (() 1 ()). I want it to be ((() 1 ()) 2 ()).
(insert x 3) ;; This prints (() 3 ()). I want it to be ((() 1 ()) 2 (() 3 ())).
Вот где я застрял.
Я предоставляю свою функцию вставки ниже.
(define (left tree) (list-ref tree 0))
(define (value tree) (list-ref tree 1))
(define (right tree) (list-ref tree 2))
(define (insert tree n)
(cond
[( null? tree ) ( list () n () )]
[(< n (cadr tree)) (list (insert (car tree) n) (cadr tree) (caddr tree))]
[(> n (cadr tree)) (list (car tree) (cadr tree) (insert (caddr tree) n))]
[else tree]
)
)
Может кто-нибудь подсказать, как я могу достичь намеченных результатов?
Ответ №1:
В вашем коде есть три проблемы. Обратите внимание, что в следующем я немного груб с кодом: я просто говорю прямо, так как пишу быстро, и я не пытаюсь быть грубым лично с вами!
Вы не определили все необходимые абстракции
Вам нужна хотя бы функция, которая создает узлы дерева, функция, которая проверяет наличие нулевого дерева, и объект, представляющий нулевые деревья. Вот более полный набор абстракций:
(define null-tree '())
(define (tree-null? tree)
(eq? tree null-tree))
(define (make-tree-node left key right)
(list left key right))
(define (left tree)
(list-ref tree 0))
(define (value tree)
(list-ref tree 1))
(define (right tree)
(list-ref tree 2))
Ваша insert
функция не использует абстракции
Он не использует те, которые вы определили, не говоря уже об отсутствующих, в результате чего получается непонятный беспорядок в стиле 1960-х годов. Вот ее версия, в которой повсюду используются абстракции (это также устраняет некоторые странные пробелы и проблемы с зависанием, которые никому не помогают читать ваш код):
(define (insert tree n)
(cond
[(tree-null? tree)
(make-tree-node null-tree n null-tree)]
[(< n (value tree))
(make-tree-node (insert (left tree) n)
(value tree)
(right tree))]
[(> n (value tree))
(make-tree-node (left tree)
(value tree)
(insert (right tree) n))]
[else tree]))
Вы не поняли, что insert
это функция
Он берет дерево и возвращает дерево, к которому добавлен элемент, вместо того, чтобы брать дерево и добавлять к нему элемент с помощью побочного эффекта. insert
никогда не изменяет ни один из своих аргументов, а скорее создает подходящее новое дерево (или возвращает существующее дерево, если добавляемый элемент уже существует).
Итак, чтобы добавить последовательность элементов, например, вам нужно построить последовательность деревьев, в insert
которые были добавлены эти элементы:
(define (insert-elements tree elts)
(if (null? elts)
tree
(insert-elements (insert tree (first elts)) (rest elts))))
Теперь:
> (insert-elements null-tree '(7 1 2 3 4 7 202 48 20 0))
'(((() 0 ()) 1 (() 2 (() 3 (() 4 ())))) 7 (((() 20 ()) 48 ()) 202 ()))
Зачем использовать абстракции
Если я изменю абстракции для деревьев:
(define null-tree '/)
(define (tree-null? tree)
(eq? tree null-tree))
(define (make-tree-node left key right)
(vector left key right))
(define (left tree)
(vector-ref tree 0))
(define (value tree)
(vector-ref tree 1))
(define (right tree)
(vector-ref tree 2))
Тогда я могу использовать точно такую же функцию с этим новым типом дерева:
> (insert-elements null-tree '(7 1 2 3 4 7 202 48 20 0))
'#(#(#(/ 0 /) 1 #(/ 2 #(/ 3 #(/ 4 /)))) 7 #(#(#(/ 20 /) 48 /) 202 /))
Комментарии:
1. Большое вам спасибо за разъяснение всего в таком подробном объяснении. Теперь, когда я думаю об этом, я действительно неправильно понял, что функция insert возвращает новое дерево. Кроме того, спасибо, что указали на дополнительные абстракции, которые делают жизнь намного проще :).
2. Что касается странного отступа, который я использовал, я очень новичок в Scheme, поэтому я делал отступы в своем коде таким образом, отчасти для того, чтобы получить некоторое сходство с другими языками, а также чтобы помочь мне понять, что происходит в коде.
3. @Anjishnu: я думаю, что заманчиво использовать функцию закрытия в строке, потому что это делает ее похожей на языки C-ish, но это действительно ошибка, потому что все эти висячие скобки — это просто шум: скобки должны почти исчезать при чтении хорошо написанного кода Lisp(думайте об этом как о чтении кода Python!)
4. Да, я больше привык к C , чем к Python. Но спасибо, что указали на это. Я позабочусь о том, чтобы код Lisp, который я напишу в будущем, был менее C-иш 🙂