因此,我正在研究Paul Graham的Common Lisp,一个问题要求创建一个合并函数,该函数可以维护要合并的列表中元素的顺序。为此,我编写了以下函数:
(defun new-union (listA listB)
(setq retset (list (car listA)))
(loop for el in (append (cdr listA) listB)
do (if (not(member el retset))
(push el (cdr (last retset)))))
(return-from new-union retset))
这将返回每个列表的唯一元素,同时保持顺序,因此如果我创建并运行:
(setq listA '(a b c a))
(setq listB '(c d e))
(new-union listA listB)
返回值为:
(A B C D E)
因此,第一件事是我得到编译器警告:"undefined variable: RETSET"
在此行:(setq retset (list (car listA)))
。另一件事是,上述方法似乎是一种更“面向对象”的处理方式,而不是使用return-from
语句的LISP方式。
是否可以以更“适合lisp”的方式编写此代码而不会出现编译器错误?
*编辑:使用@Sylwester的答案,我将函数重写如下,没有错误:
(defun new-union (listA listB)
(let ((retset (list (car listA))))
(loop for el in (append (cdr listA) listB)
do (if (not (member el retset))
(push el (cdr (last retset)))))
retset))
答案 0 :(得分:6)
setq
用于更新现有绑定,并且未创建变量retset
。标准中未指定处理方式,因此您不能依赖与之接触的代码。您可以使用defparameter
and defvar
来创建全局变量,而可以使用&aux
来创建局部变量,let
和loop
可以使用with
来创建变量。因此:
(defun new-union (list-a list-b)
(let ((retset (list (car list-a))))
...
retset
))
与使用&aux的情况相同:
(defun new-union (list-a list-b &aux (retset (list (car list-a))))
...
retset
)
也与loop with clause相同:
(defun new-union (list-a list-b)
(loop :with retset := (list (car list-a))
...
:finally (return retset)))
关于返回值。在尾部位置,评估的值为返回值。例如。
(if (< 3 4)
8
10)
这里返回8
。这意味着您的代码中(return from new-union retset)
处于尾部位置,可以只写为retset
。
现在,如果您的代码不在尾部位置,并且希望早日返回,则可以执行在尾部位置所做的操作,它将起作用。
我使用的(return retset)
从最近的未命名(nil
)块返回,而return-from
从命名的块返回。 loop
具有关键字named
,可让您选择其生成的块的名称。
要求一个lisper实现这种琐碎的功能,您会得到很多答案。有了规格和测试,我将可以完成:
(defun new-union (&rest lists &aux (hash (make-hash-table :test 'equal)))
(loop :for list :in lists
:nconc (loop :for element :in list
:if (gethash element hash t)
:collect element
:do (setf (gethash element hash) nil))))
答案 1 :(得分:3)
基于列表的版本稍好:
代码
(defun new-union (&rest lists
&aux (retset (list (caar lists)))
(rretset retset))
(dolist (list lists retset)
(dolist (el list)
(unless (member el retset)
(setf (cdr rretset) (list el)
rretset (cdr rretset))))))