Lisp 根据项目查找并删除列表项目';第三要素

Lisp 根据项目查找并删除列表项目';第三要素,lisp,common-lisp,Lisp,Common Lisp,当列表中的第一项的第三个元素与传递给函数的值匹配时,我需要查找并返回该项。然后我需要将该项目从列表中永久删除 我编写此函数就是为了实现此功能,我想知道是否有任何内置函数可以在没有这种相当混乱的实现的情况下实现相同的功能: (defun find-remove-third (x) (let ((item (first (member x *test-list* :key #'third)))) (setf *test-list* (remove item *test-list*

当列表中的第一项的第三个元素与传递给函数的值匹配时,我需要查找并返回该项。然后我需要将该项目从列表中永久删除

我编写此函数就是为了实现此功能,我想知道是否有任何内置函数可以在没有这种相当混乱的实现的情况下实现相同的功能:

(defun find-remove-third (x) 
  (let ((item (first (member x *test-list* :key #'third)))) 
    (setf *test-list*  (remove item *test-list* :test #'equal)) 
    item))
操作:

CL-USER> *test-list*
((1 2 3) (2 3 4) (3 4 5) (4 4 4) (5 4 3) (6 5 4) (2 2 2))
CL-USER> (find-remove-third 4)
(2 3 4)
CL-USER> *test-list*
((1 2 3) (3 4 5) (4 4 4) (5 4 3) (6 5 4) (2 2 2))
CL-USER> (find-remove-third 4)
(4 4 4)
CL-USER> *test-list*
((1 2 3) (3 4 5) (5 4 3) (6 5 4) (2 2 2))

例如,
pop
从列表中变异并返回,虽然它的范围更为有限,但我想知道是否可能有比我上面的函数更优雅的功能,或者这个实现是否正常且惯用?

您的实现会扫描列表两次,因此它是次优的

我认为如果没有显式循环(或者,等价地,递归),就无法编写所需的内容:

现在您可以这样定义函数:

(defun find-remove-third (x)
  (multiple-value-bind (list object)
      (pop-from-list x *test-list* :key #'third)
    (setq *test-list* list)
    object))

您的实现会扫描列表两次,因此这是次优的

我认为如果没有显式循环(或者,等价地,递归),就无法编写所需的内容:

现在您可以这样定义函数:

(defun find-remove-third (x)
  (multiple-value-bind (list object)
      (pop-from-list x *test-list* :key #'third)
    (setq *test-list* list)
    object))

编辑-删除此项似乎不正确,因此我将保留它,但正如@sds和@WillNess在评论中指出的,这有严重的问题

这是一个破坏性的版本,只扫描列表一次。它的潜在好处是,您不必硬编码正在操作的列表的名称

CL-USER> (defun find&remove (list obj &key (key #'identity) (test #'eql))
           (loop with last = nil
                 for cons on list
                 when (funcall test obj (funcall key (first cons))) do
                    (progn (setf (rest last) (rest cons))
                           (return (first cons)))
                 do (setf last cons)))

CL-USER> (defvar test-list (list (list 1 2 3)
                                 (list 3 4 5)
                                 (list 5 6 7)
                                 (list 8 9 10)))

CL-USER> (find&remove test-list 5 :key #'third)
(3 4 5)
CL-USER> test-list
((1 2 3) (5 6 7) (8 9 10))
CL-USER> (find&remove test-list 7 :key #'third)
(5 6 7)
CL-USER> test-list
((1 2 3) (8 9 10))

关键是按cons单元格而不是按项遍历列表(
loop for…on
而不是
loop for…in
),并保留一个指向我们已经查看过的列表部分的指针(
last
)。然后,当我们找到我们要找的东西时,我们将我们已经看到的
cdr
连接到下一个缺点(因此现在列表中省略了“hit”)并最终返回结果。

编辑-删除它似乎不正确,所以我将其保留,但正如@sds和@WillNess在评论中指出的,这有严重的问题

这是一个破坏性的版本,只扫描列表一次。它的潜在好处是,您不必硬编码正在操作的列表的名称

CL-USER> (defun find&remove (list obj &key (key #'identity) (test #'eql))
           (loop with last = nil
                 for cons on list
                 when (funcall test obj (funcall key (first cons))) do
                    (progn (setf (rest last) (rest cons))
                           (return (first cons)))
                 do (setf last cons)))

CL-USER> (defvar test-list (list (list 1 2 3)
                                 (list 3 4 5)
                                 (list 5 6 7)
                                 (list 8 9 10)))

CL-USER> (find&remove test-list 5 :key #'third)
(3 4 5)
CL-USER> test-list
((1 2 3) (5 6 7) (8 9 10))
CL-USER> (find&remove test-list 7 :key #'third)
(5 6 7)
CL-USER> test-list
((1 2 3) (8 9 10))

关键是按cons单元格而不是按项遍历列表(
loop for…on
而不是
loop for…in
),并保留一个指向我们已经查看过的列表部分的指针(
last
)。然后,当我们找到我们要找的东西时,我们将我们已经看到的
cdr
连接到下一个cons(因此现在列表中省略了“hit”),最后返回结果。

为什么要查找并删除它?为什么不删除它?@RainerJoswig查找它以便我可以使用它(它的其他部分,即(基于第三个元素搜索时的第一个和第二个元素)您已经实现了
prog1
。为什么查找并删除它?为什么不删除它?@RainerJoswig查找它以便我可以使用它(即它的其他部分)(在基于第三个元素进行搜索时,第一个和第二个元素)您已经实现了
prog1
。请注意,您不能以破坏性方式删除最后剩余的列表元素。此外,当调用函数对第一个列表元素进行操作时,您的函数将出错:
(查找和删除测试列表1:第三个键)
@sds last应该很容易,第一个很难。@WillNess:“剩下的最后一个列表元素”也是第一个;可以通过用第二个元素替换它来删除第一个元素,但是当没有第二个元素时,您会被卡住,因为您无法将
cons
转换为
nil
@sds是的,这就是我的意思“难”…没想到如果这是唯一的一个怎么办;你是对的。:)这就是为什么你在回答中使用了
revappend
?…我现在明白了。Yikes,很好的观点-我知道得更清楚。显然我想得不够透彻。请注意,你不能破坏性地删除最后剩下的列表元素。此外,当调用你的函数对第一个列表元素进行操作时,你的函数会出错:
(查找并删除测试列表1:key#'third)
@sds last应该很容易,first应该很难。@WillNess:“最后剩下的列表元素。”“也是第一个元素;可以用第二个元素替换第一个元素来移除第一个元素,但是当没有第二个元素时,您会被卡住,因为您无法将
cons
转换为
nil
@sds是的,这就是我所说的“硬”…没有想到如果它是唯一的元素会怎样;您是对的。:)所以这就是为什么你在回答中使用了
revappend
?…我现在明白了。哎呀,很好的观点-我知道得更清楚。显然我没有充分考虑这一点。