备注
您的代码看起来有点像受 Scheme 的启发:您将变量命名为 lst 并引入了一个局部变量,就像使用 define 一样,但在 Common Lisp 中不起作用,其中 let是范围的。
此外,您没有使用SUBSTITUTE 的返回值,这意味着任何中间结果都将被丢弃。
substitute 函数纯粹是函数式的,不会因为副作用而改变其输入。
(defun swap (lst x y)
(let (temp (nth y lst))) ;; temp is only visible inside the let
(substitute (nth x lst) (nth y lst)) ;; result is discarded
(substitute temp (nth x lst))) ;; <- the only thing actually used in swap
实现交换
这个函数有两种可能的版本,一种改变输入列表,一种不改变。您还必须决定什么是有效输入或无效输入,以及在给出无效输入时会发生什么。
在以下两种情况下,我假设x 严格低于y,如果不是这种情况,您可以通过交换x 和y 使函数更健壮,或者简单地返回@当x 和y 相等时为987654334@。
还要注意,如果x 和/或y 超出列表的限制,结果将包含NIL 值或发出错误信号,您可以在以下函数中添加更多检查以避免这些问题。
破坏性版本
这里,nswap 是破坏性版本,它使用nthcdr 下降到list x 次,到达从位置x 开始的子列表。
从这个子列表中,它将剩余的单元格数量下降到位置 y。两个列表xlist 和ylist 是分别从位置x 和y 开始的列表。
然后,我用ROTATEF交换他们的car。
(defun nswap (list x y)
(assert (< x y))
(let* ((xlist (nthcdr x list))
(ylist (nthcdr (- y x) xlist)))
(prog1 list
(rotatef (car xlist)
(car ylist)))))
功能版
纯功能的swap 可以在(copy-list lst) 上调用nswap。
然而,也可以与lst 共享一些单元格,因为ylist 其余部分之后的所有内容都保持不变。
这就是函数的工作原理,它像以前一样计算xlist 和ylist,但使用LDIFF 生成新列表(初始列表的区域)。
初始列表的不同切片与nconc 连接(它们是新列表,因此代码具有局部副作用,但在外部,输入列表未修改)。 (rest ylist) 是 nconc 的最后一个参数,不是副本。
(defun swap (list x y)
(assert (< x y))
(let* ((xlist (nthcdr x list))
(ylist (nthcdr (- y x) xlist)))
(nconc (ldiff list xlist)
(list (first ylist))
(ldiff (rest xlist) ylist)
(list (first xlist))
(rest ylist))))
例如,将:circle t 用于write 让我们看到列表的共享末尾:
(let ((list '(a b c d e f g h i)))
(write (list :original list
:swapped (swap list 1 3))
:circle t))
打印如下,#1= 标记尾部,#1# 表示结果中使用了相同的尾部:
(:ORIGINAL (A B C D . #1=(E F G H I))
:SWAPPED (A D C B . #1#))