【问题标题】:Lisp print a tree level by levelLisp 逐级打印树
【发布时间】:2022-01-16 08:01:32
【问题描述】:

我在 LISP 中有一个表示为列表的树,如下所示: (A 2 B 0 C 2 D 0 E 0)

这意味着 A 有 2 个孩子(B 和 C),B 有 0 个,依此类推...

我的任务是编写树的每一层。

我知道这篇文章:LISP Displaying binary tree level by level,但我怎样才能使用我的列表表示而不是那个帖子?

【问题讨论】:

  • 您必须以一种或另一种方式解析您的列表表示;转换为嵌套列表表示可能是最简单的,因为它可以进行递归操作。也就是写一些代码转换(A 2 B 0 C 2 D 0 E 0) --> (A (B) (C (D) (E))).

标签: list tree lisp


【解决方案1】:

一种明显的方法是首先将这种扁平但笨拙的表示解码为更明显的树,然后打印该树,我假设您知道该怎么做。这样做的一个技巧是拥有一个函数,该函数从表示中解码节点并返回解码的节点和表示的未使用尾部。这是可以做到这一点的东西。

(defun decode-one-child (from)
  (destructuring-bind (head nchilds . children/more) from
    (iterate decode ((c nchilds)
                     (cm children/more)
                     (childs '()))
      (cond
       ((zerop c)
        (values (make-node head (nreverse childs))
                cm))
       ((null cm)
        (error "ran out of children to eat"))
       (t
        (multiple-value-bind (node remaining) (decode-one-child cm)
          (decode (1- c) remaining (cons node childs))))))))

(defun decode-tree (from)
  (multiple-value-bind (decoded tail) (decode-one-child from)
    (unless (null tail)
      (error "ran out of appetite before consuming all children"))
    decoded))

请注意,这故意不是一个完整的答案,因为我怀疑这很可能是家庭作业。您将需要同时编写 make-node,并了解 iterate(不是标准 CL 的一部分)的作用。对于第二个,看看在 Scheme 中命名为 let,或者考虑一下

(iterate name ((var val) ...)
  ...)

可以翻译成

(labels ((name (var ...)
           ...))
  (name val ...))

【讨论】:

    【解决方案2】:

    不久前,另一个帐户的某个人使用相同的示例询问了一个关于这种列表表示的问题。我 wrote an answer 使用 TXR Lisp 解析该表示并转换为普通的嵌套列表。

    当多人以相同的输入或其他细节就一个非常具体的学术编程问题提出问题时,这就是家庭作业的有力证据。

    我原来答案的解析代码:

    (defun parse (syntax)
      (labels ((rec (parsym n)
                 (cons parsym
                       (collect-each ((i 0..n))
                         (match-case syntax
                           ((@sym 0 . @rest)
                            (set syntax rest)
                            (list sym))
                           ((@sym @(integerp @m) . @rest)
                            (set syntax rest)
                            (rec sym m))
                           (() (error "abrupt end in syntax"))
                           (@else (error "unhandled syntax: ~s" syntax)))))))
        (cadr (rec :root 1))))
    

    这样计算:

    1> (parse '(A 2 B 0 C 2 D 0 E 0))
    (A (B) (C (D) (E)))
    

    要打印结果的每个级别,例如:

    A
    B C
    D E
    

    在各种其他方面,我们需要广度优先搜索。简单的深度优先搜索遍历不行;这就是普通的印刷表示已经在做的事情了。

    TXR Lisp 手册,在build macro 的文档下,用几行代码给出了一个广度优先搜索遍历的示例:

    (defun bf-map (tree visit-fn)
      (buildn
        (add tree)
        (whilet ((item (del)))
          (if (atom item)
            [visit-fn item]
            (each ((el item))
              (add el))))))
    

    这并不完全符合我们的要求:它访问原子:

    7> [bf-map (parse '(A 2 B 0 C 2 D 0 E 0)) prinl]
    A
    B
    C
    D
    E
    

    所以我们当然是按级别顺序获取名称,但我们看不到级别之间的划分。

    我们需要修改这个函数以识别树的特殊语法(<name> [<tree>*]),并且对于每个级别,只打印该级别的名称。

    可能是这样的:

    (defun bf-map (tree visit-fn)
      (buildn
        (add (cons 0 tree))
        (whilet ((item (del)))
          (tree-bind (level name . children) item
            [visit-fn level name]
            (each ((ch children))
              (add (cons (succ level) ch)))))))
    

    BFS 队列中的项目现在是 agumented (level . tree) cons pair,并且由于树是 (name . children),因此队列项目是 (level name . children)。我们通过将这样的(level . tree) 项目推入队列来初始化队列,将该项目计算为(cons 0 tree)

    我们还修改了协议,以便使用两个参数调用回调函数:级别号和节点名称。

    我们可以用这个函数直接打印一些东西:

    11> (let ((oldlevel nil))
          [bf-map (parse '(A 2 B 0 C 2 D 0 E 0))
                  (lambda (level name)
                    (when (neql oldlevel level)
                      (put-line `\nlevel@level:`))
                    (print name)
                    (put-string " ")
                    (set oldlevel level))]
          (put-line))
    
    level0:
    A 
    level1:
    B C 
    level2:
    D E 
    

    或者将级别作为在从零开始的级别上索引的向量:

    13> (let ((level-vector (vec))
              (nlevels 0))
          [bf-map (parse '(A 2 B 0 C 2 D 0 E 0))
                  (lambda (level name)
                    (when (>= level nlevels)
                      (vec-push level-vector nil)
                      (upd [level-vector (pinc nlevels)] nreverse))
                    (push name [level-vector level]))]
          level-vector)
    #((A) (C B) (E D))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 2016-04-16
      相关资源
      最近更新 更多