Common Lisp中delete函数操作数组的行为及setf联用疑问
Common Lisp中
delete处理数组的行为详解 数组的本质:固定大小的连续内存块
默认用make-array创建的数组是固定大小的——数组一旦创建,它的长度(物理内存占用)就没法直接改变,因为数组是连续的内存块,不可能凭空"移除"中间的索引位置。
直接调用delete的效果
delete是破坏性操作,对数组执行时:
- 它会在原数组内部修改:把要保留的元素往前挪,覆盖被删除元素的位置;
- 原数组中没被覆盖的位置会留下旧元素的残留(比如你例子里
*x*的第三个元素还是39); 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
相关产品推荐
相关产品推荐

