gpt4 book ai didi

lisp - 使用 mapcan 卡住创建列表的重复?

转载 作者:太空宇宙 更新时间:2023-11-03 18:56:41 25 4
gpt4 key购买 nike

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

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

最佳答案

已经有一个公认的答案,但我认为需要对原始代码中出现的问题进行更多解释。 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) 解决方案,因为它不一定分配结果的中间列表。

关于lisp - 使用 mapcan 卡住创建列表的重复?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23293701/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com