这段代码有很多问题,描述它们实际上比构建一个工作示例要长,所以我将首先展示一个工作版本,然后遍历您提供的代码。不过,请通读第二部分并确保您理解原始代码中的问题。
一个工作版本
根据您的描述,您希望删除其第一个元素为 的第二个元素的每个元素。我不确定您要返回的确切内容是什么,但假设它类似于,但是使用新的第二个元素,您可以执行以下操作。我强调了那段中的某些词,因为它们对于解决这个问题很重要。你有一个,并且你想从一个序列中删除有它的东西。您可以通过调用( 或) 来做到这一点,其中是一个从序列元素中提取值以与之比较的函数。您想从中删除这些元素list
id
list
id
id
remove
delete
(remove id sequence :key <key>)
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))
您的代码有问题
有几个问题:
- 您不应该尝试定义一个名为
rem
.
- 您的代码中有语法错误。
delete
不一定具有您的代码预设的副作用。
dolist
,默认情况下,返回nil
。
更详细地说:
REM
Common 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 中遇到了类似的错误(您已经标记了问题,所以我认为这是您正在使用的实现):
[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
is的语法(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)
确实返回了一个 list (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)