0

Sometimes a list is known to contain a list of unique items, where it is useful to check if the item exists in the list, removing it if it does.

This is an inefficient way to accomplish this:

(defmacro if-pop-value (value place)
  "Destructively remove VALUE from PLACE.

With a non-nil result when the value was removed."
  `(let ((test ,value))
     (if (memq test ,place)
         (progn
           (setq ,place (delq test ,place))
           t)
       nil)))

Assuming place refers to a set of unique items, is there a more efficient way to remove the item without 2x lookups?

0

2 Answers 2

1

The problem you have here is an impedance mismatch caused by the return value. Your code returns t if the element was removed, and nil otherwise. delq, on the other hand, returns the new list. Determining the correct return value requires searching either the old list before calling delq, or the new list after calling it.

Or you could just avoid calling delq by iterating over the list yourself:

 (defmacro if-pop-value (value place)
  `(let ((test ,value)
         (list ,place))
     (if (null list)
         nil
       (if (eq test (car list))
           (prog1 t
             (setf ,place (cdr list)))
         (let ((rv nil))
           (progn
             (mapl (lambda (l)
                     (when (eq test (cadr l))
                       (setcdr l (cddr l))
                       (setq rv t)))
                   list))
             rv)))))

(The macro you just posted as an answer would work just as well.)

Or, alternatively, you could restructure your program so that it doesn’t rely on this return value. That is, it simply removes elements from the list without caring before–hand whether the element is present, or afterwards without caring whether something was removed. This may not always be possible, so I must leave it for you to consider.

0

This is a macro which removes the value from a list, resulting in t on success.

(defmacro if-pop-value (value place)
  "Destructively remove VALUE from PLACE.

With a non-nil result when the value was removed."
  `(let ((test ,value)
         (iter ,place)
         (prev nil)
         (result nil))
     (while iter
       (let ((next (cdr iter)))
         (cond
          ((eq test (car iter))
           (if prev
               (setcdr prev next)
             (setf ,place next))
           (setq result t)
           (setq iter nil))
          (t
           (setq prev iter)
           (setq iter next)))))
     result))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.