From: Jim Meehan
Subject: Re: Lisp delete
Date: 
Message-ID: <1991Aug24.112507.26338@src.dec.com>
Perhaps the most frequently asked question on this newsgroup is why
    (DELETE x y)
doesn't "change y".  Unfortunately, the most frequent answer,
    You have to write (SETQ y (DELETE x y))
is incomplete.  [NREVERSE is right up there with DELETE.]

Here's a suggestion.  Take the word "destructive" literally.  Imagine
that after (DELETE x y), y is utterly destroyed, its atoms (!) scattered
across the universe.  Smashed.  Wrecked.  Ravaged.  Lying in ruins.

But somewhere, there's this beautiful, shining object that has just
the value you want: the old, pre-apocalyptic y without that awful x
in it.  How can you find this object?  Why, it's simple: that's the
value that DELETE *returns*.  So to give poor, ravaged y this nice
value, you use SETQ, and everyone lives happily ever after.

Almost ...

It may be that the target of destruction was not named by a simple
variable, but was actually a location ("generalized variable"), such
as (CDR z) or (AREF v (INCF i)) or (GETF (GETHASH k ht '()) (READ)).
Not only can't you use SETQ on these, you can't even safely use SETF,
because there are side-effects in the last two of those examples.

This is why there is the wonderful GET-SETF-METHOD.  If you want the
result of (DELETE x <location>) to be stored back into <location>,
a simple macro will grant your wishes:

(defmacro deletef (item place &rest key-pairs)
  (let ((item-var (gensym)))
    (multiple-value-bind (vars vals stores store-form access-form)
        (get-setf-method place)
      `(let* ((,item-var ,item)
              ,@(mapcar #'list vars vals)
              (,(car stores) (delete ,item-var ,access-form ,@key-pairs)))
         ,store-form))))

Now you can write
    (DELETEF x y)
or  (DELETEF x (CDR z))
or  (DELETEF x (AREF v (INCF i)))
or  (DELETEF x (GETF (GETHASH k ht '()) (READ)))

You can write a similar macro, NREVERSEF, to handle those other
frequently asked questions.

Note that you could also write REMOVEF and REVERSEF, which would store
their results back into their 'sequence' arguments, but without
"destroying" their previous contents.  In other words, "destruction"
and "storing the result back into the source" are really independent
concepts.

For instance, if you did (SETQ x (SETQ y (LIST 1 2 3))) and subsequently
performed a destructive operation on y, then x would be affected,
because x and y both pointed to the demolished list.  But after a
non-destructive change to y, e.g., the hypothetical (REMOVEF 2 y),
x would be unaffected, whereas after (DELETEF 2 y), x would be wrecked.

--Jim