将 Common Lisp "pushnew" 改编为 return success/failure
Adapting Common Lisp "pushnew" to return success/failure
Common Lisp 宏 pushnew
returns 作为参数给出的(可能已更新的)位置列表。看来,如果您想知道给定项目是否实际被推送,则需要比较前后位置列表以查看它是否已更改。但这对我来说效率太低了,因为它会涉及重复比较两个复杂结构对象列表的等价性。
另一种可能有效的替代方法是使用 pushnew
的 :test 参数来记录是否已找到项目,因为列表元素已被扫描。如果找到该项目,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 参数是如何工作的,pushnew
的 Hyperspec 条目没有明确说明。是否保证列表元素的测试在测试 returns T 时立即停止,如所希望的那样? (或者 remove
,测试应该继续到序列的末尾。)如果测试确实继续到最后,return-from
是否是提前终止的最佳方式?
另一个问题是匿名 lambda 主体重复检查每个元素的给定 ,test
参数。这可以移到 lambda 之外吗?感谢您的任何建议。
我认为更好的方法是检查新元素是否确实 prepended pushnew
:
(unless (eq item (car place))
(pushnew item place ...)
(when (eq item (car place))
;; not found, prepended
))
请注意,即使您将 :test
传递给 pushnew
,您仍然会在 unless
和 when
中使用 eq
。
PS。请注意@coredump 评论中提到的极端情况。
Common Lisp 宏 pushnew
returns 作为参数给出的(可能已更新的)位置列表。看来,如果您想知道给定项目是否实际被推送,则需要比较前后位置列表以查看它是否已更改。但这对我来说效率太低了,因为它会涉及重复比较两个复杂结构对象列表的等价性。
另一种可能有效的替代方法是使用 pushnew
的 :test 参数来记录是否已找到项目,因为列表元素已被扫描。如果找到该项目,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 参数是如何工作的,pushnew
的 Hyperspec 条目没有明确说明。是否保证列表元素的测试在测试 returns T 时立即停止,如所希望的那样? (或者 remove
,测试应该继续到序列的末尾。)如果测试确实继续到最后,return-from
是否是提前终止的最佳方式?
另一个问题是匿名 lambda 主体重复检查每个元素的给定 ,test
参数。这可以移到 lambda 之外吗?感谢您的任何建议。
我认为更好的方法是检查新元素是否确实 prepended pushnew
:
(unless (eq item (car place))
(pushnew item place ...)
(when (eq item (car place))
;; not found, prepended
))
请注意,即使您将 :test
传递给 pushnew
,您仍然会在 unless
和 when
中使用 eq
。
PS。请注意@coredump 评论中提到的极端情况。