使用mapcan冻结创建列表的重复?

时间:2014-04-25 12:50:53

标签: lisp common-lisp

我有两个列表:(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>

为什么这个定义不起作用?

3 个答案:

答案 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)