【问题标题】:Remove list from list从列表中删除列表
【发布时间】:2013-10-21 19:36:02
【问题描述】:

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

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

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

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

【问题讨论】:

  • 您希望函数修改其参数list,以产生返回值(具体是什么?),还是两者兼而有之?
  • 此代码不会简单地返回nil,它会产生完全错误。如果您有返回 nil 的代码,请出示。 “有关您编写的代码问题的问题必须在问题本身中描述具体问题 - 并包括有效的代码来重现它。有关指导,请参阅 SSCCE.org。”

标签: lisp common-lisp


【解决方案1】:

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

工作版本

根据您的描述,您希望删除 list first 元素为 id 的 second 元素的每个元素.我不确定您要返回的确切内容,但假设它类似于list,但使用新的第二个元素,您可以执行以下操作。我强调了那段中的某些词,因为它们对于解决这个问题很重要。你有一个id,并且你想从具有id 的序列中删除一些东西。您可以通过调用(remove id sequence :key <key>) 来使用remove(或delete)来做到这一点,其中key 是一个从序列元素中提取一个值以与id 进行比较的函数。您想从(second list) 中删除那些first 为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))

您的代码存在问题

有几个问题:

  1. 您不应尝试定义名为 rem 的函数。
  2. 您的代码中有语法错误。
  3. delete 不一定具有您的代码预设的副作用。
  4. dolist,默认返回nil。

更详细的:

Common Lisp 包中已经有一个名为REM 的函数来计算余数。尝试评估您在 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 中遇到了类似的错误(您已经标记了问题,所以我认为这是您正在使用的实现):

[1]> (defun rem (x) x) ; not the same as your definition, but still a function named rem

** - Continuable Error
DEFUN/DEFMACRO(REM): #<PACKAGE COMMON-LISP> is locked
If you continue (by typing 'continue'): Ignore the lock and proceed
The following restarts are also available:
ABORT          :R1      Abort main loop

我们将重命名您的函数 %rem 以便我们可以继续,我们会看看会发生什么。当尝试在 SBCL 中编译调整后的定义时,我们会收到关于未定义变量 delete 和 equal 的警告。

; --> IF COND 
; ==>
;   (IF DELETE
;       (PROGN VAR (NTH 1 LIST))
;       NIL)
; 
; caught WARNING:
;   undefined variable: DELETE

; ==>
;   (IF EQUAL
;       (PROGN ID (NTH 0 VAR))
;       (COND (DELETE VAR (NTH 1 LIST))))
; 
; caught WARNING:
;   undefined variable: EQUAL
; 
; compilation unit finished
;   Undefined variables:
;     DELETE EQUAL
;   caught 2 WARNING conditions

在 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 的语法是(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 的值没有被修改,尽管(delete 1 x) 确实返回了一个列表(2 3)。

CL-USER> (let ((x (list 1 2 3)))
           (delete 1 x)          ; could return, e.g, (cdr x)
           x)
;=> (1 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)))))))

这段代码不太可能做很多有用的事情。一,您在迭代 (nth 1 list) 时对其进行了修改,这不太可能产生好的结果。我不确定代码到底应该做什么。由于您正在迭代 (nth 1 list),list 必须具有以下形式

(<first-element> (var1 var2 ...) ...)

既然你取了(nth 0 var),那么每个vari也必须是一个列表,所以列表的形式是

(<first-element> ((<elt10> ...) (<elt20> ...) ...) ...)

无论如何,您的dolist 仍将返回nil。 dolist 的语法是

dolist (var list-form [result-form]) declaration* {tag | statement}*

并且可选的result-form 默认为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)

【讨论】:

  • 对不起rem。该函数不是这样命名的,但我对其进行了更改以简化这里的事情,我很不幸它已经存在。 :) 。原来的名字在我的母语中很长,所以我认为最好改一下。编译时我没有收到任何错误或警告。可能是因为我在 Mac OS 中并通过 Macports 安装了 clisp 吗?我现在将更加专注地阅读您所写的内容,并希望提出解决方案。提前感谢您的出色回复!
  • 注意我是如何在 REPL 中编译的;我首先定义了函数,然后显式编译了它。即使没有警告,您也应该遇到错误,因为变量 delete 和 equal 未定义。我不认为问题中的代码可能是您正在运行的代码。
猜你喜欢
  • 1970-01-01
  • 2020-01-23
  • 2018-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多