与子列表混淆

时间:2014-01-03 08:38:03

标签: lisp

我跑过一个问题的例子,它应该确定非线性列表中任何级别的所有非数字原子的列表。

   (Defun Lis(L)
      (Cond
        ((Null L) Nil)
        ((Not (Numberp (Car L))) (Cons (Car L) (Lis (Cdr L))))
        ((Atom (Car L)) (Lis (Cdr L)))
        (T (Append (Lis (Car L)) (Lis (Cdr L))))
    ))

我举了一个例子,(Lis '(1 A ((B) 6) (2 (C 3)) D 4))应该返回(A B C D) 现在我不明白当列表的第3个元素被评估((B) 6)时如何创建列表。它将进入第二个分支并执行cons?但这不是构建((B) 6)的新列表?什么时候进入最后一个分支?我对这个算法是如何工作有点困惑,有人能说清楚吗?

2 个答案:

答案 0 :(得分:1)

如果您“反转”2个中间测试,代码可以正常工作:

(defun lis(L)
  (cond
   ((null L)          nil)
   ((numberp (car L)) (lis (cdr L)))
   ((atom (car L))    (cons (car L) (lis (cdr L))))
   (t                 (append (lis (car L)) (lis (cdr L))))))

因为(not (numberp (car L)))对于列表也是如此,因此在初始版本中,代码永远不会递归到子列表中。

答案 1 :(得分:1)

我会把它写成:

(defun tree-keep-if (predicate tree)
  "Returns the list of all non-numeric atoms at any level in a cons tree."
  (mapcan (lambda (item)
            (cond ((consp item)             (tree-keep-if predicate item))
                  ((funcall predicate item) (list item))
                  ((atom item)              nil)))
          tree))

使用它:

CL-USER > (tree-keep-if (complement #'numberp) '(1 A ((B) 6) (2 (C 3)) D 4))
(A B C D)

更复杂的版本可能会移除递归而不受堆栈大小的限制。