【问题标题】:How to remove nil values from a list in LISP?如何从 LISP 的列表中删除 nil 值?
【发布时间】:2021-12-01 08:51:01
【问题描述】:

我编写了一个函数 DFS,它以从右到左的 DFS 顺序遍历搜索树。例如,(DFS '(((((L E) F) T))) 应该返回 (T F E L)。我成功创建的以下代码可以做到这一点,但是它仍然在结果中包含 NIL 值。如何仅删除 nil 值?

; Following code returns right answer, but with nils.
; For example. running the function on 
; '((((L E) F) T)) will return ((((NIL . T) . F) . E) . L)
; but not (T F E L)

(defun DFS (tree)
  (cond ((null tree) ()) ;current node is null, return nil 
        ;Checks if car of current node is a list. If yes, recursively call DFS and append car to its cdr
        ((listp (car tree)) (DFS (append (car tree) (cdr tree))))
        ;Else, call recursively DFS on cdr of current node and append to car 
        (T (cons (DFS (cdr tree)) (car tree)))))

【问题讨论】:

  • 我认为用英文记录 COND 子句应该做什么是个好主意->。在代码中使用 cmets。否则我们必须猜测你的意图是什么。
  • 它应该如何处理,例如,((L E) (F T))?它应该返回 ((F T) (L E))、((T F) (E L)) 还是 (T F E L)?
  • 这里有一个提示->您的评论说:“附加到汽车”,但您没有附加,您正在调用缺点。附加也需要在列表上工作。

标签: lisp common-lisp depth-first-search


【解决方案1】:

如果你的树是:

(LEFT . RIGHT)

然后对(DFS RIGHT) 的递归调用应该会给你一个在右树中以相反顺序排列的叶子列表,我们称之为RL(右列表)。

同样,调用 (DFS LEFT) 应该会为您提供左侧树的叶子列表,例如称为 LL(左侧列表)。

然后,由于它们都是列表,您可以附加它们。

在您的代码中,CONS 的某些参数不一定是列表(对于 cdr)或非零符号(对于汽车)。在调用dfs之前还有一个可疑的调用append,这看起来有点太复杂了。

一个较小的例子会给出以下跟踪:

  0: (DFS (A (B)))
    1: (DFS ((B)))
      2: (DFS NIL)
      2: DFS returned NIL
      2: (DFS (B))
        3: (DFS NIL)
        3: DFS returned NIL
        3: (DFS B)
        3: DFS returned (B)
        3: (APPEND NIL (B))
        3: APPEND returned (B)
      2: DFS returned (B)
      2: (APPEND NIL (B))
      2: APPEND returned (B)
    1: DFS returned (B)
    1: (DFS A)
    1: DFS returned (A)
    1: (APPEND (B) (A))
    1: APPEND returned (B A)
  0: DFS returned (B A)

如果你解决了这个问题,你可能会注意到你经常调用append,但实际上你只是将新项目一个接一个地放在列表前面。例如,如果您可以在计算 (DFS RIGHT) 时传递列表 LL,那么您可以直接在其前面添加元素,一个一个,而不是构建中间列表并附加它们。

这可能不是一个重要的收获,特别是对于练习来说,但就练习 Lisp 而言,尝试其他解决方案是很好的。

【讨论】:

    【解决方案2】:

    我会使用尾调用递归 - 然后在acc 附近进行构建。

    使用atom 进行测试比使用listp 进行测试更惯用。

    顺便说一句,listp 有点问题。 如您所见:(listp '(a b))(listp '(a . b)) 都评估为 T。但人们会期望后者返回NIL。 所以最好使用consp 而不是listp 来减少误解。在我们就这个话题进行长时间的讨论之前,最好只使用atom 来表示难以理解的“不是列表”的情况。

    在这里,您会看到,如何逐步找到正确的解决方案:这完全取决于您将consappend 放在哪里:

    (defun dfs (tree &optional (acc '()))
      (cond ((null tree) (nreverse acc))
            ((atom (car tree)) (dfs (cdr tree) (cons (car tree) acc)))
            (t (dfs (cdr tree) (cons (dfs (car tree)) acc)))))
    
    (dfs '((((L E) F) T)))
    ;; ((((L E) F) T))
    

    cons-ing 之后我们不要nreverse

    (defun dfs (tree &optional (acc '()))
      (cond ((null tree) acc)
            ((atom (car tree)) (dfs (cdr tree) (cons (car tree) acc)))
            (t (dfs (cdr tree) (cons (dfs (car tree)) acc)))))
    
    (dfs '((((L E) F) T)))
    ;; ((T (F (E L))))
    

    这看起来更好。现在,如果它是一个列表,我们 append 而不是 cons 以摆脱不需要的括号:

    (defun dfs (tree &optional (acc '()))
      (cond ((null tree) acc)
            ((atom (car tree)) (dfs (cdr tree) (cons (car tree) acc)))
            (t (dfs (cdr tree) (append (dfs (car tree)) acc)))))
    
    (dfs '((((L E) F) T)))
    ;; (T F E L)
    

    瞧!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-15
      • 1970-01-01
      • 2017-06-03
      • 1970-01-01
      • 2017-02-09
      • 1970-01-01
      相关资源
      最近更新 更多