如何在Dr.Racket重新分配价值

时间:2017-11-06 15:31:05

标签: functional-programming scheme lisp binary-search-tree

我正在尝试在Scheme-Dr.Racket中实现二叉树,但我在插入时遇到了一些麻烦!功能

这是来源

(define (makeTree leftSubTree root rightSubTree)
  (list leftSubTree root rightSubTree))

(define (subTree part tree)
  (if (null? tree)
      '()
      (part tree)))

(define (root tree)
  (subTree cadr tree))

(define (leftSubTree tree)
  (subTree car tree))

(define (rightSubTree tree)
  (subTree caddr tree))

(define (leaf? tree)
  (and (null? (leftSubTree tree))
       (null? (rightSubTree tree))))

; (define tree (makeTree (makeTree '() 1 '()) 2 (makeTree '() 3 '()))) 

(define (member? tree element)
  (if (null? tree)
      #f
      (or (= (root tree) element)
          (member? (leftSubTree tree) element)
          (member? (rightSubTree tree) element))))

(define (insert! tree element)
  (if (null? tree)
      (set! tree (makeTree '() element '()))
      (if (leaf? tree)
          (cond
            ((> (root tree) element) (set! tree (makeTree (makeTree '() element '()) (root tree) '())))
            ((< (root tree) element) (set! tree (makeTree '() (root tree) (makeTree '() element '()))))
            ((= (root tree) element) '()))
          (cond
            ((= (root tree) element) '())
            ((> (root tree) element) (insert! (leftSubTree tree) element))
            (else  (insert! (rightSubTree tree) element))))))

据我所知,这套!函数更改对象id指向的值或

(set! tree (makeTree '() val '()))

是否会将树的值更改为具有一个节点的树 - val。 有人可以试着解释一下我为什么错了吗?

2 个答案:

答案 0 :(得分:2)

您的insert!函数只会影响名为tree自己的参数的绑定。

当函数返回时,其参数的绑定将被破坏。

您希望影响参数tree引用的内存对象的结构的更改。这不是set!所做的。它只会改变变量绑定的值。

在Scheme中有set-car!set-cdr!原语,但是Racket的列表是不可变的。可变列表使用mcons创建,而不是cons,也不是list。 Racket甚至没有set-car!和朋友,而是将其称为set-mcar!等,而且它们不适用于使用list创建的列表。

您可以使用define-struct将树表示从列表切换到结构。

或者,选择适当的#lang指令放在源文件的开头,例如: #lang r5rs所以set-car!等工作;或者使用mcons创建列表,以便在set-mcar!下工作时使用#lang racket之类的内容:

> (define y (mcons 1 (mcons 2 '())))
> y
(mcons 1 (mcons 2 '()))
> (set-mcar! y 11)
> y
(mcons 11 (mcons 2 '()))

答案 1 :(得分:1)

有什么问题?

想象一下这个C代码:

void test (int n) {
  n = 6;
}

int v = 1;
test(v);
printf("%d", v)

现在根据您的Scheme代码,您希望6是打印值,但事实并非如此。 n中的testtree中的局部变量,与insert!中的set!中的局部变量相同。当您n时,您没有改变该值,而是将标识符指向的内容重定向到新的(可能不相关的)值。指向相同值的所有其他绑定仍将指向原始值,因为它是已更改的绑定v,而不是(define (insert tree element) (if (tree-empty? tree) (make-tree tree-empty element tree-empty) (let ((value (tree-value tree)) (left (tree-left tree)) (right (tree-right tree))) (if (< value element) (make-tree (insert left element) value right) (make-tree left value (insert right element))))))

如何解决

最好不要变异。

 
(set! root (insert root 2))

您仍然可以替换这样的绑定:

(define tree-left-place values)
(define tree-right-place cddr)
(define tree-value-place cdr)
(define +root-value (list 'root))
(define root (make-tree tree-empty %root-value tree-empty))
(define (insert! tree element)
  (define (insert-helper place)
    (if (tree-empty? (place-value place))
        (set-car! place (make-tree empty-tree element empty-tree))
        (insert! (place-var place) element)))

  (let ((value (tree-value tree)))
    (cond ((eq? value +root-value) (set-car! (tree-value-place tree) element))
          ((< element value) (insert-helper (tree-left-place tree)))
          (else (insert-helper (left-right-place tree))))))

你可以改变节点..这使你成为树单例,你需要特别处理空根,因为你不能将null变成值。解决方案可以是具有空指示的特殊根。

{{1}}