英文:
What does the delete function do to an array?
问题
我试图在Common Lisp(SBCL)中使用delete
函数从数组中删除一个元素,但注意到在调用delete
之后,该数组的所有索引仍然存在(对数组执行(length arr)
的返回值未更改):
(defparameter *x* (make-array 3 :initial-contents (list 13 26 39)))
*x* ; #(13 26 39)
(delete 26 *x* :test #'equal) ; #(13 39)
*x* ; #(13 39 39)
(length *x*) ; 3
我认为*X*
的第二个元素在调用delete
后仍然可访问,因为数组是连续的内存块,无法在不创建新数组的情况下删除索引。
我的困惑来自于在delete
与setf
结合使用时。这只允许用户访问未受delete
影响的元素:
(defparameter *y* (make-array 3 :initial-contents (list 13 26 39)))
*y* ; #(13 26 39)
(setf *y* (delete 26 *y* :test #'equal)) ; #(13 39)
*y* ; #(13 39)
(length *y*) ; 2
这是否意味着当应用于数组时,delete
返回一个新数组?或者setf
调用是否会创建一个新数组,以便(setf (delete ...))
实际上与(setf (remove ...))
执行相同的操作?还是上面的*Y*
始终指向同一个数组,只是第二个元素在幕后被某种方式“忽略”了?
英文:
I am trying to delete an element from an array using the delete
function in Common Lisp (SBCL) but noticed that all the indices of this array are still present (the return value of (length arr)
on the array is unchanged) after a call to delete
:
(defparameter *x* (make-array 3 :initial-contents (list 13 26 39)))
*x* ; #(13 26 39)
(delete 26 *x* :test #'equal) ; #(13 39)
*x* ; #(13 39 39)
(length *x*) ; 3
I assume that the second element of *X*
is still accessible after calling delete
because arrays are contiguous chunks of memory where indices cannot be 'removed' without creating a new array.
My confusion comes from using setf
in conjunction with delete
. This only allows the user access to elements that were not affected by delete
:
(defparameter *y* (make-array 3 :initial-contents (list 13 26 39)))
*y* ; #(13 26 39)
(setf *y* (delete 26 *y* :test #'equal)) ; #(13 39)
*y* ; #(13 39)
(length *y*) ; 2
Does this mean that delete
returns a new array when called on arrays? Or does the setf
call create a new array so that (setf (delete ...))
effectively does the same thing as (setf (remove ...))
? Or is the *Y*
from above pointing at the same array the whole time, just the second element was somehow 'ignored' behind the scenes?
答案1
得分: 6
如果你阅读Common Lisp标准(通常是Common Lisp HyperSpec),那么它说:
delete item sequence &key from-end test test-not start end count key
=> result-sequence...
delete, delete-if, and delete-if-not are like remove, remove-if, and
remove-if-not respectively, but they may modify sequence.
"sequence" 是 "vector" 和 "list" 的超类型。因此,函数 "delete" 适用于向量和列表。
"delete item sequence" 表示必需参数。
"but they may modify sequence" 意味着该函数可能修改作为参数提供的原始序列。"may modify" 意味着实现可能在行为上有所不同。
但无论如何,你需要使用返回的 "result sequence"。这也相对容易记住,因为许多(但不是所有)函数通常返回结果,并且更倾向于以计算结果的方式思考,而不是作为带有副作用的过程。
请注意,Common Lisp中的向量是可变对象。可以更改其内容,而无需分配新对象。还请注意:Common Lisp提供了具有破坏性功能的函数,这些函数可以修改参数对象。对于某些函数,这种行为是有意的,对于某些函数,它们是不应使用的副作用。"delete" 是这样一种后者的函数:参数序列可以被修改,不应再使用该对象。
示例1:
(defparameter *x* (make-array 3 :initial-contents (list 13 26 39)))
上面的代码创建了一个长度为3的向量对象,并使用提供的内容进行初始化。变量 "x" 指向该对象。
*x* ; #(13 26 39)
上述代码:变量 "x" 仍然指向最初创建的对象。
(delete 26 *x* :test #'equal) ; #(13 39)
上述代码:从 "delete" 调用返回了 #(13 39)
。这个值未被使用,只是返回。变量 "x" 仍然指向最初创建的向量对象。该对象可能已被修改。
*x* ; #(13 39 39)
上面的代码:变量 "x" 仍然指向最初创建的对象。该对象已经被 "delete" 调用破坏性地修改。
(length *x*) ; 3
上述代码:变量 "x" 仍然指向最初创建的对象。它的长度仍然是3。
示例2:
(defparameter *y* (make-array 3 :initial-contents (list 13 26 39)))
上面的代码创建了一个长度为3的向量对象,并使用提供的内容进行初始化。变量 "y" 指向该对象。
*y* ; #(13 26 39)
上面的代码:变量 "y" 仍然指向最初创建的对象。
(setf *y* (delete 26 *y* :test #'equal)) ; #(13 39)
上面的代码:变量 "y" 被设置为从 "delete" 调用返回的对象: #(13 39)
。
*y* ; #(13 39)
上面的代码:变量 "y" 指向从 "delete" 返回的新对象。
(length *y*) ; 2
上述代码:变量 "y" 仍然指向从 "delete" 返回的新对象。该对象的长度为2。
英文:
If you read the Common Lisp standard (usually the Common Lisp HyperSpec), then it says:
> delete item sequence &key from-end test test-not start end count key
> => result-sequence
>
> ...
>
> delete, delete-if, and delete-if-not are like remove, remove-if, and
> remove-if-not respectively, but they may modify sequence.
sequence
is the supertype of vector
and list
. Thus the function delete
works for vectors and lists.
delete item sequence
denotes the required arguments.
but they may modify sequence
then means that the function may modify the original sequence provided as an argument. may modify means that an implementations can differ in behavior.
But in any case you need to use the returned result sequence. That's also relatively easy to remember, since many (but not all) functions typically return results and it is preferred to think in terms of computed results and not as a procedure with side effects.
Note that vectors in Common Lisp are mutable objects. One can change their contents, without allocating a new object. Also note: Common Lisp provides destructive functions which may modify the argument objects. For some functions the behavior is intended and for some functions they are side effects not to be used. delete
is such a latter function: the argument sequence can be changed and that object should no longer be used.
Example 1:
(defparameter *x* (make-array 3 :initial-contents (list 13 26 39)))
Above creates a vector object of length 3 with the provided contents. The variable *x*
points to this object.
*x* ; #(13 26 39)
Above: The variable *x*
still points to the originally created object.
(delete 26 *x* :test #'equal) ; #(13 39)
Above: #(13 39)
is returned from the delete
call. This value is not used, just returned. The variable *x*
still points to the originally created vector object. That object might have been changed.
*x* ; #(13 39 39)
Above: The variable *x*
still points to the originally created object. The object has been destructively changed by the delete
call.
(length *x*) ; 3
Above: The variable *x*
still points to the originally created object. It's length is still 3.
Example 2:
(defparameter *y* (make-array 3 :initial-contents (list 13 26 39)))
Above creates a vector object of length 3 with the provided contents. The variable *y*
points to this object.
*y* ; #(13 26 39)
Above: The variable *y*
still points to the originally created object.
(setf *y* (delete 26 *y* :test #'equal)) ; #(13 39)
Above: the variable *y*
is set to the returned object from the delete
call: #(13 39)
.
*y* ; #(13 39)
Above: The variable *y*
points to the new, from delete
returned, object.
(length *y*) ; 2
Above: The variable *y*
still points to the new, from delete
returned, object. The length of that object is 2.
答案2
得分: 4
delete
是一项具有破坏性的操作,它可能(但不一定)会修改其操作的原始序列。返回的是生成的新序列。如果在使用 delete
(或类似的破坏性操作)后,将原始序列视为正确的结果(就像你的第一个示例中那样),那将是一个错误。你必须始终使用由 delete
返回的序列(就像你的第二个示例中那样)。
“这是否意味着调用 delete
时会返回一个新数组?”
你应该将返回值视为一个新数组。delete
确实会构造其结果,但它允许在该过程中修改或破坏输入序列。返回的序列可能与输入序列在 eq
意义上相同,也可能不同。
setf
不会创建新序列,在使用 remove
而不是 delete
的情况下,remove
不 允许修改或破坏其输入序列。从 remove
返回的结果也可能与输入序列在 eq
意义上相同,也可能不同。
无论是使用非破坏性的 remove
还是具有破坏性的 delete
,你必须使用操作的结果返回的序列。在使用 remove
的情况下,你仍然可以使用原始输入序列,因为它可以可靠地保持不变,但在使用 delete
的情况下,在操作后不应使用输入序列,因为它可能已经被修改。
英文:
delete
is a destructive operation which may (but is not required to) modify the original sequence upon which it operates. The resulting sequence is returned. It is a mistake to use delete
(or similar destructive operations) and then to treat the original sequence as if it is the correct result (as in your first example). You must always use the sequence which has been returned by delete
(as in your second example).
"Does this mean that delete returns a new array when called on arrays?"
You should probably think of the return value as a new array. delete
does construct its result, but it is allowed to modify or destroy the input sequence as part of that process. The returned sequence may or may not be identical to the input sequence in the eq
sense.
setf
does not create a new sequence, and in the case of using remove
instead of delete
, remove
is not allowed to modify or destroy its input sequence. The result returned from remove
also may or may not be identical to the input sequence.
Whether you use the non-destructive remove
or the destructive delete
, you must work with the returned sequence to use the result of the operation. In the case of remove
you may still work with the original input sequence since it is reliably known to be intact, but in the case of delete
you should not use the input sequence after the operation since it may have been altered.
答案3
得分: 4
DELETE 是一种破坏性 操作。这意味着该函数可以修改输入序列以生成其输出,因此,您应该仅使用函数的输出,必须将输入视为丢失,不得再使用它。
重要的是要理解为什么存在这种“破坏性”行为:这是为了提高效率;当您调用破坏性运算符时,您允许实现以更高效的方式使用输入来生成输出,而不是使用非破坏性运算符。
其推论是:不应该使用破坏性运算符来产生副作用,您必须将输入视为已经彻底丢失。
话虽如此,这并不意味着实现不能重新使用您的输入来生成输出。请看这个使用SBCL的小实验:
CL-USER> (defparameter *x* (make-array 3 :initial-contents (list 13 26 39)))
*X*
CL-USER> (defparameter *y* (delete 26 *x* :test #'=))
*Y*
CL-USER> *x*
#(13 39 39)
CL-USER> *y*
#(13 39)
CL-USER> (eq *x* *y*)
NIL
但现在看:
CL-USER> (defparameter *x* (make-array 3 :element-type 'number
:fill-pointer 3
:initial-contents (list 13 26 39)))
*X*
CL-USER> *x*
#(13 26 39)
CL-USER> (defparameter *y* (delete 26 *x* :test #'=))
*Y*
CL-USER> *y*
#(13 39)
CL-USER> *x*
#(13 39)
CL-USER> (eq *x* *y*)
T
英文:
DELETE is a destructive operation. That means that the function can modify the input sequence to produce its output, and so, you should only use the output of the function and you must consider the input as lost and you must not use it anymore.
It's important to understand why such "destructive" behavior is available in destructive operators : it is for efficiency purpose; When you call a destructive operator, you're allowing the implementation to do what it wants with the input to produce the output in a more efficient way than by using a non-destructive operator.
The corollary is that : you must not use destructive operators for their side-effects, you must consider your input as definitively lost.
That said, that doesn't mean implementations can't reuse your input to produce the output. See this little experiment with SBCL :
CL-USER> (defparameter *x* (make-array 3 :initial-contents (list 13 26 39)))
*X*
CL-USER> (defparameter *y* (delete 26 *x* :test #'=))
*Y*
CL-USER> *x*
#(13 39 39)
CL-USER> *y*
#(13 39)
CL-USER> (eq *x* *y*)
NIL
But now see:
CL-USER> (defparameter *x* (make-array 3 :element-type 'number
:fill-pointer 3
:initial-contents (list 13 26 39)))
*X*
CL-USER> *x*
#(13 26 39)
CL-USER> (defparameter *y* (delete 26 *x* :test #'=))
*Y*
CL-USER> *y*
#(13 39)
CL-USER> *x*
#(13 39)
CL-USER> (eq *x* *y*)
T
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论