汽车:合同违规预期:对?给定:()在二叉搜索树中删除

时间:2017-10-04 19:01:19

标签: scheme

我不明白我是如何违反合同的。似乎当我创建一个bst它没有2个空列表时它只有()。

这是我的删除方法:

;Returns the binary search tree representing bst after removing x where f and g are predicates as defined in bst-contains.
    (define (bst-remove bst f g x)
      ;if empty return empty
      (cond ((empty? bst) (bst-create-empty)))
      ;else if equal then check right if right is empty then pull from left
      (cond ((g (car bst) x) (cond ((empty? (caddr bst)) (cond ((empty? (cadr bst)) (bst-create-empty))
                                                               (else (car(cadr bst)))))
                                   ;if right isnt empty then remove from left
                                   (else(bst-create (bst-max-right (caddr bst)) (cadr bst) (bst-remove (caddr bst) f g (bst-max-right (caddr bst))))))) 
                             (else (bst-create (car bst) (bst-remove (cadr bst) f g x) (bst-remove (caddr bst) f g x)))))

我的bst-create和bst-create-empty:

;Returns an empty binary search tree.
(define (bst-create-empty)
  '())

;Returns a binary search tree having the specified root value, with left-subtree left and right-subtree right.
(define (bst-create root left right)
  (list root left right))

我给它的代码是

(bst-remove (bst-create 5 (bst-create 6 (bst-create-empty) (bst-create-empty)) (bst-create 4 (bst-create-empty) (bst-create-empty))) < = 6)

我得到的错误是汽车:合同违规预期:对?给出:()

3 个答案:

答案 0 :(得分:1)

你有Scheme,特别是cond都错了。如果你在一个过程的主体中有两个语句,如:

(define (test lst)
  first-expression
  tail-expression)

很明显tail-expression会跟随评估并放弃first-expression的任何结果。除非first-expression有副作用,否则它就是死代码。您的cond表达式(cond ((empty? bst) (bst-create-empty)))是死代码,因为无论结果如何,它都不会成为结果的一部分,因为Scheme将无条件地评估第二个cond。它会(car bst)抛出错误。

The correct way to have multiple returns are by one expression:

(cond 
  (test1 consequent1)
  (test2 consequent2)
  (test3 consequent3)
  (else alternative))

毋庸置疑,之前的所有测试都是否定的,如果test3为真,那么您就知道test1test2都有负面结果。您还知道,如果评估consequent1,则不会评估其他术语或测试。它停留在第一个好人。

在特定情况下,代码可能如下所示:

(define (bst-remove bst f g x)
  (cond ((empty? bst)
         (bst-create-empty))
        ((not (g (car bst) x))
         (bst-create (car bst) (bst-remove (cadr bst) f g x) (bst-remove (caddr bst) f g x)))
        ((not (empty? (caddr bst)))
         (bst-create (bst-max-right (caddr bst)) (cadr bst) (bst-remove (caddr bst) f g (bst-max-right (caddr bst)))))
        ((empty? (cadr bst))
         (bst-create-empty))
        (else
         (caadr bst))))

使用嵌套的if也可以,但是就像嵌套的cond一样,它更难以读取代码。请注意,我否定了一些测试,因为他们只有一个替代方案,但后来有几个测试。通过否定我可以有一个结果并继续测试同一cond中的其他情况。

答案 1 :(得分:0)

您在评论中将第二个cond称为else if,但它不是其他 - 如果,它只是一个if。也就是说,即使第一个条件为真,也检查第二个条件,在这种情况下,条件的car部分会导致此错误。

要解决此问题,您应该将这两个条件作为单个cond的一部分,在这种情况下,它实际上就像else if一样。

答案 2 :(得分:0)

看起来你误解了cond,或者可能是一般的方案。

函数应用程序的结果是函数体中最后一个表达式的值(如果你正在做一些有副作用的事情,那么拥有多个表达式的唯一原因)和cond表达式以与其他Scheme表达式完全相同的方式进行计算。

因此表达式(cond ((empty? bst) (bst-create-empty)))不会返回一个空树,它会被计算并产生一个空树,并且该树被丢弃。
然后继续评估下一个cond,这在树空时是个坏主意。

另一个问题是该函数应该生成一个树,但(car (cadr bst))不会。

如果您定义了一些有用的访问器函数:

(define bst-value car)
(define bst-left cadr)
(define bst-right caddr)

然后这些线条显然是错误的:

(cond ((empty? (bst-left bst)) (bst-create-empty))
       (else (bst-value (bst-left bst)))))

修复它,(合理地)清楚地表明整个表达

(cond ((empty? (bst-left bst)) (bst-create-empty))
       (else (bst-left bst))))

相当于

(bst-left bst)

现在你有了

(cond ((empty? (bst-right bst)) (bst-left bst))
      ( else make a tree...

但这里缺乏对称性;当然,如果左子树是空的,结果应该是类似方式的整个右子树。

所以,

(cond ((empty? (bst-right bst)) (bst-left bst))
       (empty? (bst-left bst)) (bst-right bst))
       ( else make a tree...

但现在我们可以发现另一个问题:即使在这些情况下,我们也需要在完成之前进入子树。

我会在这里离题,因为太多的访问者重复和cond s会使代码难以理解。

使用几个let s(并删除未使用的f参数),我最终得到了这个:

(define (bst-remove tree g x)
  (if (empty? tree)
      (bst-create-empty)
      ;; If the tree isn't empty, we need to recurse.
      ;; This work is identical for all the cases below, so
      ;; lift it up here.
      (let ([new-left  (bst-remove (bst-left tree) g x)]
            [new-right (bst-remove (bst-right tree) g x)])
        ;; Build an appropriate tree with the new subtrees.
        (if (g (bst-value tree) x)
            (cond [(empty? new-left) new-right] ;; If either new subtree is empty,
                  [(empty? new-right) new-left] ;;  use the other.
                  ;; The complicated case. Get the new node value from the
                  ;; right subtree and remove it from there before using it.
                  [else (let ([new-value (bst-max-right new-right)])
                           (bst-create new-value
                                       new-left
                                       (bst-remove new-right g new-value)))])
            ;; The straightforward case.
            (bst-create (bst-value tree) 
                         new-left 
                         new-right)))))