LISP 适当的方法 Return 从函数中获取值

LISP Appropriate Way to Return Value From Function

所以我正在研究 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))

这 return 是每个列表的唯一元素,同时保持顺序所以如果我创建和 运行:

(setq listA '(a b c a))
(setq listB '(c d e))
(new-union listA listB)

return是:

(A B C D E)

所以第一件事是我在这一行收到编译器警告:"undefined variable: RETSET"(setq retset (list (car listA)))。另一件事是,上述方法似乎是一种更 "object-oriented" 的做事方式,而不是使用 return-from 语句的 LISP 方式。

是否可以以更 "lisp-appropriate" 的方式编写此代码而不会出现编译器错误?

*编辑:使用@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))

setq 用于更新现有绑定,您的变量 retset 未创建。标准中没有指定如何处理它,因此您不能依赖接触它的代码。您可以使用 defparameter and defvar 创建全局变量,而您可以在函数中使用 &aux 创建局部变量,letloop 可以使用 with 创建变量。因此:

(defun new-union (list-a list-b)
  (let ((retset (list (car list-a))))
    ...
    retset
    ))

和这个一样 using &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))) 

大约 return 值。在尾部位置,评估的值是 returned 值。例如。

(if (< 3 4)
    8
    10)

这里 8 是 returned。这意味着 (return from new-union retset) 在你的代码中,它在尾部位置,可以只写 retset.

现在,如果您有不在尾部位置的代码并且您希望尽早 return 您可以执行在尾部位置所做的事情,它会起作用。

我使用最近的未命名 (nil) 块中的 (return retset) returns,而命名块中的 return-from returns。 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))))

稍微好一点的基于列表的版本:

  • 联合两个以上的列表
  • 不使用 LAST
  • 仍然使用 MEMBER
  • 不使用 APPEND
  • DOLIST returns 值 RETSET

代码

(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))))))