You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Common Lisp中delete函数操作数组的行为及setf联用疑问

Common Lisp中delete处理数组的行为详解

数组的本质:固定大小的连续内存块

默认用make-array创建的数组是固定大小的——数组一旦创建,它的长度(物理内存占用)就没法直接改变,因为数组是连续的内存块,不可能凭空"移除"中间的索引位置。

直接调用delete的效果

delete是破坏性操作,对数组执行时:

  1. 它会在原数组内部修改:把要保留的元素往前挪,覆盖被删除元素的位置;
  2. 原数组中没被覆盖的位置会留下旧元素的残留(比如你例子里*x*的第三个元素还是39);
  3. delete会返回一个数组视图(子数组),这个视图只包含前n个有效元素(n是删除后剩下的元素数),但原数组本身的长度和物理大小完全没变。

对应你的第一个例子:

(defparameter *x* (make-array 3 :initial-contents (list 13 26 39)))
(delete 26 *x* :test #'equal) ; 返回只包含前2个元素的视图#(13 39)
*x* ; 原数组被修改为#(13 39 39),长度还是3
(length *x*) ; 3,因为原数组的物理长度没变化

和setf联用的逻辑

当你用setf把delete的返回值赋值给原变量时:

  • 变量不再指向原来的固定长度数组,转而指向delete返回的那个子数组视图;
  • 这个子数组视图的长度等于剩余元素的数量,所以(length *y*)会返回2;
  • 原来的3长度数组还存在(如果没有其他引用就会被垃圾回收),但*y*现在指向的是长度为2的视图。

对应你的第二个例子:

(defparameter *y* (make-array 3 :initial-contents (list 13 26 39)))
(setf *y* (delete 26 *y* :test #'equal)) ; *y*现在指向长度为2的子数组
*y* ; #(13 39)
(length *y*) ; 2,因为当前指向的视图长度是2

澄清:delete vs remove

  • remove是纯函数:不会碰原数组,直接创建并返回一个包含保留元素的新数组;
  • delete是破坏性操作:会尽量在原序列上修改(链表改指针,数组移元素),返回的结果可能是原序列的修改版,也可能是新的视图/数组,取决于序列类型。

对数组来说,(setf arr (delete ... arr))看起来和(setf arr (remove ... arr))效果一样,但底层不同:delete先改原数组再返回视图,remove直接生成新数组。

内容的提问来源于stack exchange,提问作者Connor Krenzer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 00:32:06