Lisp HUG Maillist Archive

Can not (delete) last remaining element from a list

Hello,

I have a class with a list of streams :

(defclass server ()
	((streams :accessor streams :initform ())))


When a new stream is registered, the stream is pushed on the list:

	(push stream (streams server))


When a stream is discarded, it is deleted:

	(delete stream (streams server))


All works well, I can add and remove elements.
But when the list reaches one remaining element, this element is never removed:
For example if I have the streams list as:

(#<COMM:SOCKET-STREAM 200939FB>)

And I remove #<COMM:SOCKET-STREAM 200939FB>.
The result of (delete) returns nil - as expected - but my server's slot 'streams has still the element I wish to remove.

I feel like I am missing something trivial.
Can someone help me understand what is going wrong?


Best,
Camille


Re: Can not (delete) last remaining element from a list

On Tue, May 31, 2011 at 11:58, Camille Troillard <camille@osculator.net> wrote:
> Hello,
>
> I have a class with a list of streams :
>
> (defclass server ()
>        ((streams :accessor streams :initform ())))
>
>
> When a new stream is registered, the stream is pushed on the list:
>
>        (push stream (streams server))
>
>
> When a stream is discarded, it is deleted:
>
>        (delete stream (streams server))
>
>
> All works well, I can add and remove elements.

You are discarding the updated field value. It's only by chance that
it appears to work for all but the last element.

Use instead: (setf (streams server) (delete stream (streams server))
or something equivalent.

Cheers
P.


Re: Can not (delete) last remaining element from a list

Camille Troillard <camille@osculator.net> writes:

> Hello,
>
> I have a class with a list of streams :
>
> (defclass server ()
> 	((streams :accessor streams :initform ())))
>
>
> When a new stream is registered, the stream is pushed on the list:
>
> 	(push stream (streams server))
>
>
> When a stream is discarded, it is deleted:
>
> 	(delete stream (streams server))
>
>
> All works well, I can add and remove elements.
> But when the list reaches one remaining element, this element is never removed:
> For example if I have the streams list as:
>
> (#<COMM:SOCKET-STREAM 200939FB>)
>
> And I remove #<COMM:SOCKET-STREAM 200939FB>.
> The result of (delete) returns nil - as expected - but my server's slot 'streams has still the element I wish to remove.
>
> I feel like I am missing something trivial.
> Can someone help me understand what is going wrong?
>
>
> Best,
> Camille

Destructive functions are not meant to be called for their side-effects.
They are destructive for reasons of efficiency. That means you should
use the return value of a destructive function and not count on any
modification of the argument(s). E.g.

(setf (streams server) (delete stream (streams server)))

Ansi Common Lisp by Paul Graham have a nice section about this (12.4 on
p. 201) and incidentally uses delete as an example.

Nico


Re: Can not (delete) last remaining element from a list

Camille Troillard <camille@osculator.net> writes:

> On 31 mai 2011, at 15:18, Nico de Jager wrote:
>
>> Destructive functions are not meant to be called for their side-effects.
>> They are destructive for reasons of efficiency. That means you should
>> use the return value of a destructive function and not count on any
>> modification of the argument(s). E.g.
>> 
>> (setf (streams server) (delete stream (streams server)))
>> 
>> Ansi Common Lisp by Paul Graham have a nice section about this (12.4 on
>> p. 201) and incidentally uses delete as an example.
>
> Thank you very much Nico for the reference to the book.
> I'll read that.
>
> Actually I got confused because I believed that delete was updating a
> "place", in the spirit of what (push) does.

Also, delete is a function, not a macro.


> Someone kindly pointed out that I've misinterpreted the spec, and that
> delete updates the given argument, not a place.

It is important to understand that delete is not required to modify the
argument, and even if it does, the argument (after the call) and the
result may not be the same. The CLHS entry for delete states (emphasis
added by me): 

"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."

and

"... delete, delete-if, and delete-if-not return a sequence of the same
type as sequence that has the same elements except that those in the
subsequence bounded by start and end and satisfying the test have been
deleted. Sequence may be destroyed and used to construct the result;
however, the result _might_ or _might_ _not_ be identical to sequence."

Nico


Re: Can not (delete) last remaining element from a list

Camille Troillard <camille@osculator.net> writes:

> On 31 mai 2011, at 15:18, Nico de Jager wrote:
>
>> Destructive functions are not meant to be called for their side-effects.
>> They are destructive for reasons of efficiency. That means you should
>> use the return value of a destructive function and not count on any
>> modification of the argument(s). E.g.
>> 
>> (setf (streams server) (delete stream (streams server)))
>> 
>> Ansi Common Lisp by Paul Graham have a nice section about this (12.4 on
>> p. 201) and incidentally uses delete as an example.
>
> Thank you very much Nico for the reference to the book.
> I'll read that.
>
> Actually I got confused because I believed that delete was updating a
> "place", in the spirit of what (push) does.  Someone kindly pointed
> out that I've misinterpreted the spec, and that delete updates the
> given argument, not a place.

You could define a modify macro:

    (defun delete* (seq item &rest keys &key &allow-other-keys)
       (apply (function delete)  item seq keys))

    (define-modify-macro deletef (place item &rest keys) delete*
      "Delete the ITEM from the sequence at PLACE.  
    Notice the order of the first two parameters is inversed with respect to
    DELETE.")


So you can write:

    (deletef (streams server) stream)




Note also that your original code wasn't conforming, because
implementations are allowed to define DELETE as:

    (defun delete (item seq &rest keys &key &allow-other-keys)
       (apply (function remove) item seq keys))

so just calling most destructive CL functions may have strictly no side
effect.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
A bad day in () is better than a good day in {}.


Re: Can not (delete) last remaining element from a list

Camille,

Sounds like others already sorted this out for you. But I'll just add that there is a nice discussion and some examples comparing delete and remove in Practical Common Lisp too that I found helpful in conjunction with ANSI Common Lisp and the Hyperspec referenced earlier: http://www.gigamonkeys.com/book/they-called-it-lisp-for-a-reason-list-processing.html (its about halfway down the referenced page).

Best,

Lang

On Tue, May 31, 2011 at 6:19 AM, Camille Troillard <camille@osculator.net> wrote:

On 31 mai 2011, at 15:18, Nico de Jager wrote:

> Destructive functions are not meant to be called for their side-effects.
> They are destructive for reasons of efficiency. That means you should
> use the return value of a destructive function and not count on any
> modification of the argument(s). E.g.
>
> (setf (streams server) (delete stream (streams server)))
>
> Ansi Common Lisp by Paul Graham have a nice section about this (12.4 on
> p. 201) and incidentally uses delete as an example.

Thank you very much Nico for the reference to the book.
I'll read that.

Actually I got confused because I believed that delete was updating a "place", in the spirit of what (push) does.  Someone kindly pointed out that I've misinterpreted the spec, and that delete updates the given argument, not a place.


Best Regards,
Camille


Updated at: 2020-12-10 08:37 UTC