Lisp 从列表中删除列表

Lisp 从列表中删除列表,lisp,common-lisp,Lisp,Common Lisp,我正在编写一个简单的函数,需要从列表中删除某个元素。列表中有3个列表,我想在第二个列表中搜索给定的值。第二个列表的元素也是列表(id x y) 我的函数接收一个列表和id作为参数,它必须从具有该id的第二个列表中删除该元素 (defun rem (list id) (dolist (var (nth 1 list)) (cond (equal id (nth 0 var)) (delete var (nth 1 list)))) ) 我搜索给定列表的第二个列表,

我正在编写一个简单的函数,需要从列表中删除某个元素。列表中有3个列表,我想在第二个列表中搜索给定的值。第二个列表的元素也是列表(id x y)

我的函数接收一个
列表
id
作为参数,它必须从具有该id的第二个列表中删除该元素

(defun rem (list id)
  (dolist (var (nth 1 list))
    (cond (equal id (nth 0 var))
          (delete var (nth 1 list))))
)

我搜索给定列表的第二个列表,当我找到id为
id
的元素时,我将其删除。问题是我总是得到
NIL
。我也尝试过函数
remove
,但结果是一样的。

这段代码有很多问题,描述它们实际上比构建一个工作示例要长,因此我将首先展示一个工作版本,然后浏览您提供的代码。请通读第二部分,确保您理解原始代码中的问题

工作版本 根据您的描述,您希望删除第一个元素为
id
list
第二个元素的每个元素。我不确定您想要返回的是什么,但是假设它类似于
list
,但是使用新的第二个元素,您可以执行如下操作。我强调了那一段中的某些词,因为它们对解决这个问题很重要。您有一个
id
,并且希望从具有该
id
的序列中删除内容。您可以通过调用
(删除id序列:键)
(或
delete
)来执行此操作,其中
key
是一个函数,它从序列元素中提取一个值以与
id
进行比较。您想从
(第二个列表)
中删除那些其
第一个
id
的元素。你会用

(remove id (second list) :key 'first)
这样做。在上下文中,您将得到如下函数:

(defun bex-remove (list id)
  (list (first list)
        (remove id (second list) :key 'first)
        (third list)))
下面是一个例子:

(bex-remove '((1 2 3 4)                 ; values don't matter
              ((id-a x1 y1)
               (id-b x2 y2)
               (id-a x3 y3)
               (id-b x4 y4))
              (5 6 7 8))                ; values don't matter
            'id-a)

;=> ((1 2 3 4) ((ID-B X2 Y2) (ID-B X4 Y4)) (5 6 7 8))
代码的问题 有几个问题:

  • 您不应该尝试定义名为
    rem
    的函数
  • 代码中有语法错误
  • delete
    不一定具有代码预先假定的副作用
  • 默认情况下,
    dolist
    返回
    nil
  • 更详细地说:

    公共Lisp包中已经有一个名为的函数,用于计算余数。尝试在SBCL中评估您的定义表示错误:

    Lock on package COMMON-LISP violated when setting fdefinition of
    REM while in package COMMON-LISP-USER.
       [Condition of type SYMBOL-PACKAGE-LOCKED-ERROR]
    See also:
      SBCL Manual, Package Locks [:node]
      Common Lisp Hyperspec, 11.1.2.1.2 [:section]
    
    在CLISP中也会出现类似的错误(您已经用它标记了问题,因此我假设您使用的是实现):

    在CLISP中,您必须先编译,然后才能得到类似的警告:

    CL-USER> (defun %rem (list id)
               (dolist (var (nth 1 list))
                 (cond (equal id (nth 0 var))
                       (delete var (nth 1 list))))
               )
    %REM
    CL-USER> (compile '%rem)
    WARNING: in %REM : EQUAL is neither declared nor bound,
             it will be treated as if it were declared SPECIAL.
    WARNING: in %REM : DELETE is neither declared nor bound,
             it will be treated as if it were declared SPECIAL.
    %REM
    2
    2
    
    的语法是
    (cond(test expr*)*)
    ,这意味着每个测试及其关联表达式都需要用括号括起来。更新以解决此问题,我们现在有:

    (defun %rem (list id)
      (dolist (var (nth 1 list))
        (cond 
          ((equal id (nth 0 var))
           (delete var (nth 1 list))))))
    
    当我们编译时,SBCL中仍然会出现一些警告,但CLISP不会生成类似的警告,即使在编译期间:

    ; in: DEFUN %REM
    ;     (DELETE VAR (NTH 1 LIST))
    ; 
    ; caught STYLE-WARNING:
    ;   The return value of DELETE should not be discarded.
    ; 
    ; caught STYLE-WARNING:
    ;   The return value of DELETE should not be discarded.
    ; 
    ; compilation unit finished
    ;   caught 2 STYLE-WARNING conditions
    
    这告诉我们,您确实需要保存
    delete
    的结果
    delete
    可以以任意方式修改列表,但根本不需要修改任何内容。例如,在下面的代码中,变量
    x
    的值不会被修改,尽管
    (删除1 x)
    会返回一个列表
    (2 3)

    所以你可能想写的是:

    (defun %rem (list id)
      (dolist (var (nth 1 list))
        (cond                                    ; or (when (equal id (nth 0 var))
          ((equal id (nth 0 var))                ;      (setf (nth 1 list) ...))
           (setf (nth 1 list) 
                 (delete var (nth 1 list)))))))
    
    这段代码不太可能有什么用处。第一,在迭代时修改
    (第n个1列表)
    ,这不太可能有好的结果。我不确定代码到底应该做什么。由于您正在迭代
    (第n个1个列表)
    列表必须具有

    (<first-element> (var1 var2 ...) ...)
    
    无论如何,您的
    dolist
    仍将返回
    nil
    。的语法是

    可选的
    结果表单默认为
    nil
    。我不确定你到底想要返回什么,但可能是列表,在这种情况下你会这样做

    (dolist (var list list)
      …)
    
    例如:

    (let ((list (list 1 2 3)))
      (dolist (x list)                ; return default (nil)
        (+ x x)))
    ;=> NIL
    
    (let ((list (list 1 2 3)))
      (dolist (x list (reverse list)) ; return something
        (+ x x)))
    ;=> (3 2 1)
    

    这段代码有很多问题,描述它们实际上比构建一个工作示例要长,因此我将首先展示一个工作版本,然后浏览您提供的代码。请通读第二部分,确保您理解原始代码中的问题

    工作版本 根据您的描述,您希望删除第一个元素为
    id
    list
    第二个元素的每个元素。我不确定您想要返回的是什么,但是假设它类似于
    list
    ,但是使用新的第二个元素,您可以执行如下操作。我强调了那一段中的某些词,因为它们对解决这个问题很重要。您有一个
    id
    ,并且希望从具有该
    id
    的序列中删除内容。您可以通过调用
    (删除id序列:键)
    (或
    delete
    )来执行此操作,其中
    key
    是一个函数,它从序列元素中提取一个值以与
    id
    进行比较。您想从
    (第二个列表)
    中删除那些其
    第一个
    id
    的元素。你会用

    (remove id (second list) :key 'first)
    
    这样做。在上下文中,您将得到如下函数:

    (defun bex-remove (list id)
      (list (first list)
            (remove id (second list) :key 'first)
            (third list)))
    
    下面是一个例子:

    (bex-remove '((1 2 3 4)                 ; values don't matter
                  ((id-a x1 y1)
                   (id-b x2 y2)
                   (id-a x3 y3)
                   (id-b x4 y4))
                  (5 6 7 8))                ; values don't matter
                'id-a)
    
    ;=> ((1 2 3 4) ((ID-B X2 Y2) (ID-B X4 Y4)) (5 6 7 8))
    
    代码的问题 有几个问题:

  • 您不应该尝试定义名为
    rem
    的函数
  • 代码中有语法错误
  • delete
    不一定具有代码预先假定的副作用
  • 默认情况下,
    dolist
    返回
    nil
  • 更详细地说:

    公共Lisp包中已经有一个名为的函数,用于计算余数。尝试在SBCL中评估您的定义表示错误:

    Lock on package COMMON-LISP violated when setting fdefinition of
    REM while in package COMMON-LISP-USER.
       [Condition of type SYMBOL-PACKAGE-LOCKED-ERROR]
    See also:
      SBCL Manual, Package Locks [:node]
      Common Lisp Hyperspec, 11.1.2.1.2 [:section]
    
    在CLISP中也会出现类似的错误(您用它标记了问题,所以
    (let ((list (list 1 2 3)))
      (dolist (x list)                ; return default (nil)
        (+ x x)))
    ;=> NIL
    
    (let ((list (list 1 2 3)))
      (dolist (x list (reverse list)) ; return something
        (+ x x)))
    ;=> (3 2 1)