LISP从函数中返回值的适当方法

Eli*_*red 0 functional-programming sbcl common-lisp

因此,我正在通过Paul Graham的Common Lisp工作,并且有一个问题要求创建一个union函数,该函数维护列表中元素的组合顺序.为此,我写了以下函数:

(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))
Run Code Online (Sandbox Code Playgroud)

这将返回每个列表的唯一元素,同时保持顺序,因此如果我创建并运行:

(setq listA '(a b c a))
(setq listB '(c d e))
(new-union listA listB)
Run Code Online (Sandbox Code Playgroud)

回报是:

(A B C D E)
Run Code Online (Sandbox Code Playgroud)

所以第一件事是我得到一个编译器警告:"undefined variable: RETSET"在这一行:(setq retset (list (car listA))).另一件事是上面的方法似乎是一种更"面向对象"的做事方式而不是LISP方式的return-from陈述.

是否可以在没有编译器错误的情况下以更"适合"的方式编写此代码?

*编辑:使用@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))
Run Code Online (Sandbox Code Playgroud)

Syl*_*ter 6

setq是更新现有绑定,并且retset不创建您的变量.标准中没有指定如何处理它,因此您不能依赖于触及它的代码.您可以使用defparameterdefvar创建全局变量,同时可以&aux在函数中创建局部变量,letloop可以使用with.从而:

(defun new-union (list-a list-b)
  (let ((retset (list (car list-a))))
    ...
    retset
    ))
Run Code Online (Sandbox Code Playgroud)

与使用&aux相同:

(defun new-union (list-a list-b &aux (retset (list (car list-a))))
  ...
  retset
  )
Run Code Online (Sandbox Code Playgroud)

loop with子句一样:

(defun new-union (list-a list-b)
  (loop :with retset := (list (car list-a))
     ...
     :finally (return retset))) 
Run Code Online (Sandbox Code Playgroud)

关于返回值.在尾部位置,评估的值是返回值.例如.

(if (< 3 4)
    8
    10)
Run Code Online (Sandbox Code Playgroud)

8是返回.这意味着(return from new-union retset)在你的代码中,它处于尾部位置,可能只是编写retset.

现在,如果您的代码不在尾部位置并且您希望提前返回,那么您可以执行您在尾部位置所做的操作,它将起作用.

我使用的(return retset)那个从最近的unnamed(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))))
Run Code Online (Sandbox Code Playgroud)