我正在为考试做准备,我几乎做了所有事情,但是我不理解该练习。如何使函数能够从特定值在二叉树中搜索路径。
这里是一个示例:
(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
(find-path tree 4)
(1 2 4)
答案 0 :(得分:1)
我开始草绘一些代码-
(define (find t q) ;; find q in t
(path empty) ;; the return path will be a list, we'll start it off as an empty list
(if (empty? t) ;; fundamental laws: always check for empty list first
#f ;; if the tree is empty, there is nothing to find, we use #f to signal this
(if (eq? q (car t)) ;; otherwise we can check if the node matches q ...
;; wups we can't do eq? test yet, it's possible `(car t)` is a list of nodes
))
我怎么看?我看一下我们的输入列表-
(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
empty?
(car tree)
(cdr tree)
1
(2 (3) (4))
(5 (6 (7) (8)) (9))
1
,所以我认为我可以拿到eq?
并立即检查它是否匹配q
list?
eq?
情况
修复我的嘘声-
(define (find t q)
(path empty)
(if (empty? t)
#f
(if (list? (car t))
;; when the node is a list of nodes
(if (eq? q (car t))
;; when the node matches q
;; when the node does not match q
)))
将折叠if
链接到cond
以获得更好的可读性-
(define (find t q)
(path empty)
(cond ((empty? t)
#f)
((list? (car t))
;; when the node is a list of nodes
)
((eq? q (car t))
;; when the node matches q
)
(else
;; when the node does not match q
))
该代码更加扁平,现在更易于阅读。其中一些空白很难填写,但我被第二个空白吸引了。当q
等于(car t)
时,表示我们找到了一个匹配项,现在该返回path
-
(define (find t q)
(path empty)
(cond ((empty? t)
#f)
((list? (car t))
;; when the node is a list of nodes
;; we'll come back to this ...
)
((eq? q (car t))
(cons q path)) ;; return the path with the final node
(else
;; when the nodes does not match q
;; and save this for later too ...
))
好的,还不错。因此,我检查了(car t)
与q
匹配的时间,现在我不得不说当不匹配时会发生什么。当(car t)
不匹配时,我将其添加到path
并以某种方式检查q
是否与节点的任何子节点(cdr t)
-
(define (find t q)
(path empty)
(cond ((empty? t)
#f)
((list? (car t))
;; when node is a list of nodes
;; we'll come back to this ...
)
((eq? q (car t))
(cons q path))
(else
;; add the node to the path ...
(cons (car t) path)
;; check the node's children for a match
(find (cdr t) q)
;; this doesn't quite work ...
))
我遇到一种情况,我们需要用新节点更新path
,并且需要调用没有find
参数的path
。为了解决这个问题,我引入了一个循环,该循环允许我们使用指定的任何参数来重复计算表达式-
(define (find t q)
(let loop ;; lazily and sloppily insert a named loop
((path empty) ;; initialize the parameters that will change
(t t))
(cond ((empty? t) ;; the expression to repeat, (cond ...)
#f)
((list? (car t))
;; when the node is a list of nodes
)
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path) ;; updated path
(cdr t)))) ;; updated tree
else
子句教我如何与节点的子代(节点的列表)进行匹配。毫无疑问,这将使处理代码中的最后一个空白变得更加容易,当节点是节点列表时,该怎么办! -
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
;; we could just recur the loop with
(loop path
(car t))
;; but what about (cdr t) in this case?
(loop path
(cdr t))
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path)
(cdr t))))
这里的最后一个问题是我要检查两(2)个列表; (car t)
被确定为列表,而(cdr t)
被确定为列表。我必须检查他们两个。一种简单的解决方案是将两个loop
调用与or
组合在一起。如果一个loop
返回#f
,则将检查另一个-
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
(or (loop path ;; or represents dysjunction!
(car t))
(loop path
(cdr t))))
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path)
(cdr t))))
修正括号,运行自动压头-
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
(or (loop path
(car t))
(loop path
(cdr t))))
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path)
(cdr t))))))
(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
(find tree 4)
;; '(4 2 1)
(find tree 8)
;; (8 6 5 1)
(find tree 9)
;; (9 5 1)
观察到结果是相反的,因为实际上path
是按相反的顺序构造的。返回路径的退出条件只需要在返回-
reverse
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
(or (loop path
(car t))
(loop path
(cdr t))))
((eq? q (car t))
(reverse (cons q path))) ;; don't forget to reverse!
(else
(loop (cons (car t) path)
(cdr t))))))
(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
(find tree 4)
;; '(1 2 4)
(find tree 8)
;; (1 5 6 8)
(find tree 9)
;; (1 5 9)