Lisp 如何仅使用cons将值追加到现有列表中?

Lisp 如何仅使用cons将值追加到现有列表中?,lisp,common-lisp,cons,Lisp,Common Lisp,Cons,在lisp中,我试图仅使用基本函数将值追加到列表中:setq cons car和cdr 我可以向后创建列表,但我很难弄清楚如何按顺序推送它们,如何才能做到这一点 (setq list NIL) (setq list (cons 1 list)) (setq list (cons 2 list)) (setq list (cons 3 list)) Result: (3 2 1) 这是一个令人惊讶的广泛话题。首先,通常情况下,您不会将单个项目附加到列表中。处理类似您可能遇到的情况的通常方法是,

在lisp中,我试图仅使用基本函数将值追加到列表中:setq cons car和cdr

我可以向后创建列表,但我很难弄清楚如何按顺序推送它们,如何才能做到这一点

(setq list NIL)
(setq list (cons 1 list))
(setq list (cons 2 list))
(setq list (cons 3 list))

Result:
(3 2 1)

这是一个令人惊讶的广泛话题。首先,通常情况下,您不会将单个项目附加到列表中。处理类似您可能遇到的情况的通常方法是,首先将想要的值收集到一个列表中,然后反转该列表以获得正确的顺序:

(let ((my-list '()))
  (dotimes (i 10)
    (setf my-list (cons i my-list)))
  (nreverse my-list)) ; NOTE: nreverse destructively modifies its argument
;;=> (0 1 2 3 4 5 6 7 8 9)
当然,您可以自己编写一个函数,将值“附加”到列表中,并返回一个新值:

(defun my-append (value list)
  (if (null list)
      (cons value '()) ; or use (list value)
      (cons (first list) (my-append value (rest list)))))
您可以将其转换为尾部递归变量,但这并不能解决此函数的主要问题:它需要遍历每个元素的整个列表才能附加它。因此,这是在
O(n)
中,
n
是列表的长度

另一个问题是,这个函数需要考虑很多因素,也就是说,它会生成很多额外的cons单元,从而导致内存管理的大量工作。不过,我们可以通过破坏性地修改其论点来解决这个问题

另请参见已经是Common Lisp一部分的函数

要给您一个概述,您可以

  • 按“错误”顺序收集值(使用
    cons
    ),然后破坏性地反转该列表()
  • 按“错误”的顺序收集值,然后创建一个新的反向列表,其中的值按正确的顺序排列()
  • 实际上,
    append
    将您的值添加到列表中,允许可能的二次或更糟糕的运行时间
  • 实际上,将值附加到列表中,破坏性地修改列表结构()。这是对
    append
    的改进,因为您不需要分配大量新的cons单元格,但仍然存在运行时问题

基于的“hack”,我编写了使用这种“列表”操作的函数。它由一个cons组成,其
car
指向列表的开头(即列表),其
cdr
指向列表的最后一个cons单元格。尽管最后一个单元格总是
(nil.nil)
,但简化了附加值:

首先,将最后一个单元格的
car
设置为新值,然后将
cdr
设置为新单元格
(nil.nil)
,最后返回新的“列表”:


这是一个令人惊讶的广泛话题。首先,通常情况下,您不会将单个项目附加到列表中。处理类似您可能遇到的情况的通常方法是,首先将想要的值收集到一个列表中,然后反转该列表以获得正确的顺序:

(let ((my-list '()))
  (dotimes (i 10)
    (setf my-list (cons i my-list)))
  (nreverse my-list)) ; NOTE: nreverse destructively modifies its argument
;;=> (0 1 2 3 4 5 6 7 8 9)
当然,您可以自己编写一个函数,将值“附加”到列表中,并返回一个新值:

(defun my-append (value list)
  (if (null list)
      (cons value '()) ; or use (list value)
      (cons (first list) (my-append value (rest list)))))
您可以将其转换为尾部递归变量,但这并不能解决此函数的主要问题:它需要遍历每个元素的整个列表才能附加它。因此,这是在
O(n)
中,
n
是列表的长度

另一个问题是,这个函数需要考虑很多因素,也就是说,它会生成很多额外的cons单元,从而导致内存管理的大量工作。不过,我们可以通过破坏性地修改其论点来解决这个问题

另请参见已经是Common Lisp一部分的函数

要给您一个概述,您可以

  • 按“错误”顺序收集值(使用
    cons
    ),然后破坏性地反转该列表()
  • 按“错误”的顺序收集值,然后创建一个新的反向列表,其中的值按正确的顺序排列()
  • 实际上,
    append
    将您的值添加到列表中,允许可能的二次或更糟糕的运行时间
  • 实际上,将值附加到列表中,破坏性地修改列表结构()。这是对
    append
    的改进,因为您不需要分配大量新的cons单元格,但仍然存在运行时问题

基于的“hack”,我编写了使用这种“列表”操作的函数。它由一个cons组成,其
car
指向列表的开头(即列表),其
cdr
指向列表的最后一个cons单元格。尽管最后一个单元格总是
(nil.nil)
,但简化了附加值:

首先,将最后一个单元格的
car
设置为新值,然后将
cdr
设置为新单元格
(nil.nil)
,最后返回新的“列表”:

有一种技术(我不记得它是否有一个特定的名称),它包括创建一个带有任何
car
值的初始cons单元格,并保留一个指向
cdr
的指针。然后,追加包括
setf
ing指针的
cdr
和推进指针。您想要的列表将随时成为初始cons单元格的
cdr

? (progn (setq lst (cons 'head nil)) (setq lst-p lst) (cdr lst))
NIL
? (progn (setf (cdr lst-p) (cons 1 nil)) (setq lst-p (cdr lst-p)) (cdr lst))
(1)
? (progn (setf (cdr lst-p) (cons 2 nil)) (setq lst-p (cdr lst-p)) (cdr lst))
(1 2)
? (progn (setf (cdr lst-p) (cons 3 nil)) (setq lst-p (cdr lst-p)) (cdr lst))
(1 2 3)
因此,这只使用了
setq
cons
cdr
,但也使用了
setf
,有一种技术(我不记得它是否有一个特定的名称),它包括用任何
car
值创建一个初始cons单元格,并保留一个指向
cdr
的指针。然后,追加包括
setf
ing指针的
cdr
和推进指针。您想要的列表将随时成为初始cons单元格的
cdr

? (progn (setq lst (cons 'head nil)) (setq lst-p lst) (cdr lst))
NIL
? (progn (setf (cdr lst-p) (cons 1 nil)) (setq lst-p (cdr lst-p)) (cdr lst))
(1)
? (progn (setf (cdr lst-p) (cons 2 nil)) (setq lst-p (cdr lst-p)) (cdr lst))
(1 2)
? (progn (setf (cdr lst-p) (cons 3 nil)) (setq lst-p (cdr lst-p)) (cdr lst))
(1 2 3)
因此,这只使用了
setq
cons
cdr
,而且
setf

Lisp列表是单独链接的列表。实际上,这里没有“列表”,只有一堆cons单元格,也称为pairs,我们按照惯例将其解释为列表(cons x y)生成一对(x.y)。作为一种特殊情况,当cons的cdr是一个列表时,例如在(x.(12 3))中,我们将该对写为(x 12 3)。而符号(defun mapkar (function list &optional (result '())) (if (endp list) (nreverse result) (mapkar function (rest list) (cons (funcall function (first list)) result))))
(defun range (n)
  (loop :for i :below n
        :collect i))

(defun range (n)
  (let ((list ()))
    (dotimes (i n)
      (push i list))
    (reverse list)))
(defun own-reverse (list &optional (acc ()))
  (if (endp list)
      acc
      (own-reverse (rest list)
                   (cons (first list) acc))))