我有两个列表:(1 2 3)
和(a b)
,我需要创建类似(1 2 3 1 2 3)
的内容。结果是第一个列表的串联次数与第二个列表中的元素一样多次。我应该使用一些函数(maplist
/ mapcar
/ mapcon
等)。这正是我需要的,虽然我需要将第一个列表作为参数传递:
(mapcan #'(lambda (x) (list 1 2 3)) (list 'a 'b))
;=> (1 2 3 1 2 3)
当我尝试将其抽象为函数时,Allegro会冻结:
(defun foo (a b)
(mapcan #'(lambda (x) a) b))
(foo (list 1 2 3) (list 'a 'b))
; <freeze>
为什么这个定义不起作用?
答案 0 :(得分:4)
已经有一个已接受的答案,但我认为有关原始代码中出现问题的更多解释是有序的。 mapcan
将一个函数应用于列表的每个元素,以生成一堆破坏性地连接在一起的列表。如果破坏性地将列表与自身连接起来,则会得到一个循环列表。例如,
(let ((x (list 1 2 3)))
(nconc x x))
;=> (1 2 3 1 2 3 1 2 3 ...)
现在,如果你有更多的连接,你就无法完成,因为要将某些东西连接到列表的末尾,需要走到列表的末尾。所以
(let ((x (list 1 2 3)))
(nconc (nconc x x) x))
; ----------- (a)
; --------------------- (b)
(a)终止,并返回列表(1 2 3 1 2 3 1 2 3 ...)
,但是(b)无法终止,因为我们无法到达(1 2 3 1 2 3 ...)
的末尾以便将内容添加到最后。
现在留下了为什么
的问题(defun foo (a b)
(mapcan #'(lambda (x) a) b))
(foo (list 1 2 3) '(a b))
导致冻结。由于(a b)
中只有两个元素,因此相当于:
(let ((x (list 1 2 3)))
(nconc x x))
应终止并返回无限列表(1 2 3 1 2 3 1 2 3 ...)
。事实上,确实如此。问题是打印 REPL中的列表将挂起。例如,在SBCL中:
CL-USER> (let ((x (list 1 2 3)))
(nconc x x))
; <I manually stopped this, because it hung.
CL-USER> (let ((x (list 1 2 3)))
(nconc x x) ; terminates
nil) ; return nil, which is easy to print
NIL
如果您将*print-circle*
设置为true,则可以看到第一个表单中的结果:
CL-USER> (setf *print-circle* t)
T
CL-USER> (let ((x (list 1 2 3)))
(nconc x x))
#1=(1 2 3 . #1#) ; special notation for reading and
; writing circular structures
调整代码以消除有问题的行为的最简单的方式(即,最少的更改次数)是在lambda函数中使用copy-list
:
(defun foo (a b)
(mapcan #'(lambda (x)
(copy-list a))
b))
这也优于(reduce 'append (mapcar ...) :from-end t)
解决方案,因为它不一定分配中间结果列表。
答案 1 :(得分:1)
你可以
(defun f (lst1 lst2)
(reduce #'append (mapcar (lambda (e) lst1) lst2)))
然后
? (f '(1 2 3) '(a b))
(1 2 3 1 2 3)
答案 2 :(得分:0)
经验法则是确保提供给mapcan
(和破坏性朋友)的功能创建列表,否则您将进行循环。这同样适用于提供给其他破坏性功能的参数。通常,如果函数使它们成为线性更新,那么它是最好的。
这将有效:
(defun foo (a b)
(mapcan #'(lambda (x) (copy-list a)) b))
以下是一些替代方案:
(defun foo (a b)
;; NB! apply sets restrictions on the length of b. Stack might blow
(apply #'append (mapcar #'(lambda (x) a) b))
(defun foo (a b)
;; uses loop macro
(loop for i in b
append a))
我真的不明白为什么b不能成为一个数字?你真的把它当作教会号码,所以我想我会这样做:
(defun x (list multiplier)
;; uses loop
(loop for i from 1 to multiplier
append list))
(x '(a b c) 0) ; ==> nil
(x '(a b c) 1) ; ==> (a b c)
(x '(a b c) 2) ; ==> (a b c a b c)
;; you can still do the same:
(x '(1 2 3) (length '(a b))) ; ==> (1 2 3 1 2 3)