LISP:为什么我不能在空列表上使用 cons?

LISP: Why can't I use cons on an empty list?

我正在尝试通过添加到一个空列表来构造一个唯一的元素列表,使用 LISP 中的以下代码:

    ;;;MEMSET - return T if an atom is a top-level member of a set, else NIL
    ;;;This is needed for the makeset function
    (DEFUN MEMSET (ATM L)
        (COND ( ( NULL L) NIL )
              ( (EQL ATM(CAR L)) T )
              ( T    (MEMSET ATM (CDR L)) )
        )
    )
    (DEFUN MAKESET(SET1)
        (DO ((UNIQ ()))
            ( (NULL SET1) UNIQ) 
            (COND ( (NOT (MEMSET (CAR SET1) UNIQ))
                      (CONS (CAR SET1) UNIQ)
                  )
            )
        (SETF SET1 (CDR SET1))
        ) 
    )

当我调用 (makeset '(a b b a c d b a)) 时,这个特定代码导致 NIL - 它应该导致 (a b c d),不考虑顺序) - 但在我看来它应该从SET1 尚未在每次迭代的 UNIQ 中。您不能添加到在 do 循环中声明的空列表中,还是我遇到了其他问题?顺便说一句,我正在使用clisp。

1.使用正确的格式

询问有关 Common-Lisp 的问题时请使用正确的格式。 例如,请参阅谷歌搜索 "common lisp formatting conventions".

返回的前三页

以下是应用于您的函数的常规格式示例:

;;; Memset - return T if an atom is a top-level member of a set, else NIL
;;; This is needed for the makeset function

(defun memset (atm l)
  (cond ((null l) nil)
        ((eql atm (car l)) t)
        (t (memset atm (cdr l)))))

(defun makeset (set1)
  (do ((uniq ()))
      ((null set1) uniq)
    (cond ((not (memset (car set1) uniq))
           (push (car set1) uniq)))
    (setf set1 (cdr set1))))

2。尽可能使用原始函数

你的两个函数在 Common-Lisp 中都是原始的。

remove-duplicates returns a list without duplicate elements, member 检查一个元素是否属于一个集合。

3。你函数中的错误

如果您仍然想使用您的功能,而不是 remove-duplicates,问题就出在这里。

如果你想修改一个列表,你应该使用一个修改某些东西的函数。 cons 函数构建了一个新的对,但是 没有修改任何东西。因此,在您的表单 (cons (car set1) uniq) 中,您向 uniq 添加了一个新元素,因为您获得了一个 新列表 ,其中 (car set1) 作为第一个元素,并且unique 的元素作为列表的其余部分,但是这个新列表会立即被丢弃,因为它没有分配给任何东西。

您可以通过使用宏 setf 将新值分配给局部变量 uniq 来更改此设置,方法如下:

(setf uniq (cons (car set1) uniq))

或者您可以通过使用宏 push:

编写等效的形式
(push (car set1) uniq)

最后,请注意 makeset 函数中的 cond 可以替换为更简洁的 when,从而得到此函数:

(defun makeset (set1)
  (do ((uniq nil))
      ((null set1) uniq)
    (when (not (memset (car set1) uniq))
      (push (car set1) uniq))
    (setf set1 (cdr set1))))

所有更新都可以通过DO中的步进形式完成。不需要 PUSHSETF:

(defun makeset (set1)
  (do ((uniq nil (if (memset (car set2) uniq)
                      uniq
                    (cons (car set2) uniq)))
       (set2 set1 (cdr set2)))
      ((null set2) uniq)))