Lisp 在Scheme中查找从树的根到叶的所有路径

Lisp 在Scheme中查找从树的根到叶的所有路径,lisp,scheme,racket,Lisp,Scheme,Racket,给定一棵树,我想找到从根到每片叶子的路径 因此,对于这棵树: D / B / \ A E \ C-F-G 具有从根(A)到叶(D、E、G)的以下路径: 如果我将上面的树表示为(A(bde)(C(fg)),那么函数G会执行以下操作: (define (paths tree) (cond ((empty? tree) '()) ((pair? tree) (map (lambda (path)

给定一棵树,我想找到从根到每片叶子的路径

因此,对于这棵树:

    D
   /
  B
 / \ 
A   E
 \
  C-F-G
具有从根(A)到叶(D、E、G)的以下路径:

如果我将上面的树表示为
(A(bde)(C(fg))
,那么函数
G
会执行以下操作:

(define (paths tree)
  (cond ((empty? tree)
         '())
        ((pair? tree)
         (map (lambda (path)
                (if (pair? path)
                    (cons (car tree) path)
                    (cons (car tree) (list path))))
              (map2 paths (cdr tree))))
        (else
         (list tree))))

(define (map2 fn lst)
  (if (empty? lst)
      '()
      (append (fn (car lst))
              (map2 fn (cdr lst)))))
但这看起来完全错了。我已经有一段时间不需要做这种思考了,但我觉得应该有一种更整洁的方式来做。任何更好的解决方案(任何语言)的想法都将不胜感激


编辑-将Svante的解决方案映射到Scheme中提供:

(define (paths tree)
  (if (pair? tree)
      (append-map (lambda (node)
              (map (lambda (path)
                     (cons (car tree) path))
                   (paths node)))
            (cdr tree))
      (list (list tree))))

这比我原来的要简洁得多。

我认为您可以将示例树定义为(左-右根)每个树都是一个列表。因此,您的示例树是:(D(B(A()(C()(F()(G)))E()),这更容易遍历

您需要一个树搜索算法。广度优先或深度优先遍历都可以工作,在本例中,这两种遍历没有区别,因为您需要对整个树进行爬网。无论何时到达叶子,只要将当前路径存储在结果中即可。

我的Common Lisp更流利

(defun paths (tree)
  (if (atom tree)
      (list (list tree))
      (mapcan (lambda (node)
                (mapcar (lambda (path)
                          (cons (car tree) path))
                        (paths node)))
              (cdr tree))))

CL-USER> (paths '(A (B D E) (C (F G))))
((A B D) (A B E) (A C F G))

Svante答案的R5RS翻译:

(define (accumulate op init seq) (define (iter ans rest) (if (null? rest) ans (iter (op ans (car rest)) (cdr rest)))) (iter init seq)) (define (flatten seq) (accumulate append '() seq)) (define (flatmap op seq) (flatten (map op seq))) (define (atom? x) (not (pair? x))) (define (paths tree) (if (atom? tree) (list (list tree)) (flatmap (lambda (node) (map (lambda (path) (cons (car tree) path)) (paths node))) (cdr tree)))) (定义(累积操作初始顺序) (定义(iter和rest) (如果为空,则为rest) ans (国际热核实验堆(操作系统(汽车休息)) (cdr休息) (国际热核聚变实验堆初始) (定义(展平顺序) (加上“()序号)) (定义(平面图操作顺序) (展平(地图操作顺序))) (定义(原子?x) (不是(第x对))) (定义(路径树) (如果(原子树) (列表(列表树)) (平面图(lambda(节点) (地图(lambda(路径) (cons(汽车树)路) (路径节点))) (cdr树)
我想你误读了我的图表——A是树的根。另外(这个问题可能还不清楚)一个节点可以有两个以上的分支,所以我认为你的方法行不通。我刚刚意识到我误读了你的图表。我想说,如果g函数起作用,你应该使用它:-)这是相当整洁的-而且可能更正确,因为它处理具有单个节点的树的方式:(路径'a)=>((a)),而(g'a)=>(a)。CL的mapcan与我上面的map2大致相同吗?你知道内置的Scheme函数吗?我对Scheme知之甚少,但我猜像mapcan这样的函数是可用的。不过,也许它被视为易于实现。您的
map2
在原则上似乎是等效的,但它的尾部调用错误,因此当列表太长时,它将导致堆栈崩溃。也许您应该使用累加器/反向惯用法。Scheme与SRFI-1中的
mapcan
等效,即
append map
。 (define (accumulate op init seq) (define (iter ans rest) (if (null? rest) ans (iter (op ans (car rest)) (cdr rest)))) (iter init seq)) (define (flatten seq) (accumulate append '() seq)) (define (flatmap op seq) (flatten (map op seq))) (define (atom? x) (not (pair? x))) (define (paths tree) (if (atom? tree) (list (list tree)) (flatmap (lambda (node) (map (lambda (path) (cons (car tree) path)) (paths node))) (cdr tree))))