Lisp 如何仅使用cons将值追加到现有列表中?
在lisp中,我试图仅使用基本函数将值追加到列表中:setq cons car和cdr 我可以向后创建列表,但我很难弄清楚如何按顺序推送它们,如何才能做到这一点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) 这是一个令人惊讶的广泛话题。首先,通常情况下,您不会将单个项目附加到列表中。处理类似您可能遇到的情况的通常方法是,
(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
- 实际上,将值附加到列表中,破坏性地修改列表结构()。这是对
的改进,因为您不需要分配大量新的cons单元格,但仍然存在运行时问题append
基于的“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
- 实际上,将值附加到列表中,破坏性地修改列表结构()。这是对
的改进,因为您不需要分配大量新的cons单元格,但仍然存在运行时问题append
基于的“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))))