不久前,另一个帐户的某个人使用相同的示例询问了一个关于这种列表表示的问题。我 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))