Макрос Common Lisp pushnew
возвращает (возможно обновленный) список мест, заданный в качестве аргумента.Похоже, что если вы хотите узнать, был ли данный элемент фактически сдвинут или нет, вам нужно сравнить список мест до и после, чтобы увидеть, изменился ли он.Но это слишком неэффективно для моего использования, так как это потребовало бы многократного сравнения двух списков объектов со сложной структурой для эквивалентности.
Другая альтернатива, которая может сработать, - это использовать аргумент: test для pushnew
, чтобы записать, еслиэлемент еще не найден, так как элементы списка сканируются.Если элемент найден, T может быть возвращено как второе кратное значение, иначе NIL.Следующий макрос пытается сделать это:
(defmacro pushnew+p (item place &key test (key #'identity) &aux old?)
"Same as pushnew, but also returns whether item was pushed."
`(values (pushnew ,item ,place
:test (lambda (item element)
(if ,test
(setf old? (funcall ,test item element))
(setf old? (eql item element))))
:key ,key)
(not old?)))
Кажется, он работает нормально, так как
(defparameter x '(1 2 3))
(pushnew+p 0 x) -> (0 1 2 3), T
(pushnew+p 2 x) -> (0 1 2 3), NIL
(defparameter y '((1) (2) (3)))
(pushnew+p '(0) y :test #'equal) -> ((0) (1) (2) (3)), T
(pushnew+p '(2) y :test #'equal) -> ((0) (1) (2) (3)), NIL
Однако я не уверен в том, как работает аргумент: test и Hyperspecзапись для pushnew
прямо не говорит.Гарантируется ли, что тестирование элементов списка прекращается, как только тест возвращает T, как требуется?(В качестве альтернативы для remove
тестирование должно продолжаться до конца последовательности.) Если тестирование продолжается до конца, будет ли return-from
лучшим способом завершить работу раньше?
Другая проблема заключается вчто анонимное лямбда-тело повторно проверяет заданный аргумент ,test
для каждого элемента.Можно ли это перенести за пределы лямбды?Спасибо за любой совет.