Lisp& rest参数和递归调用

时间:2018-01-04 20:18:16

标签: function recursion lisp common-lisp variadic-functions

我有以下Common Lisp函数:

(defun test(A &rest indexes)
  (if (null (first indexes))
      A
    (test (nth (+ 1 (first indexes)) A) (rest indexes))
  )
)

据我所知&rest参数被视为函数体中的列表,但是从

开始

(rest indexes)还会返回一个列表,我会将嵌套列表作为参数。

例如(test '("a" "b" "c" ("d" "e")) 3 1 6 7)

会在第二次调用时导致索引为((1 6 7))

有没有办法在没有这个问题的情况下通过我的列表?

2 个答案:

答案 0 :(得分:2)

基本样式规则:不要使用&rest参数进行列表处理功能。

为什么呢?允许Common Lisp实现仅支持CALL-ARGUMENTS-LIMIT个参数的值。这个数字是50或更大,具体取决于实施。

这意味着您的函数可能在某些实现过程列表中不超过50个项目。

更好:将列表作为单独的参数传递。

(defun test (A indexes)
   ...)

(test '("a" "b" "c" ("d" "e")) '(3 1 6 7))

错误的解决方案:不要使用apply,因为它无法解决有限的参数列表问题。

CLISP

[1]> call-arguments-limit
4096
[2]> (defun l1 (&rest l) l)
L1
[3]> (apply #'l1 (loop repeat 5000 collect 1))

*** - APPLY: too many arguments given to
      #<FUNCTION L1 (&REST L)
         (DECLARE (SYSTEM::IN-DEFUN L1))
         (BLOCK L1 L)>
The following restarts are available:
ABORT          :R1      Abort main loop

答案 1 :(得分:1)

rest是一个与first配对的访问者函数,为您提供第一个元素和列表的其余部分。 restcdr相同。

&rest是一个lambda list keyword,它会在其后面的变量名中包含其余参数。

你真的在寻找apply。想象一下,我创建了一个可以接受0个或更多数字参数并将它们加在一起的函数:

(defun add (&rest numbers)
  (apply #'+ numbers))

Apply可以使用两个以上的参数。第一个是调用函数,除了最后一个函数之外的所有函数都是放在最后一个arguments元素前面的额外参数。保证实现支持50个参数或者函数可以采用的参数数量,特定实现支持50以上。

(apply #'+ 1 2 '(3 4 5)) ; ==> 15

现在由&restapply递归会产生效率低下的代码,因此您应该使用更高阶函数,循环宏或帮助:

;; higher order function
(defun fetch-indexes (a &rest indexes)
  (mapcar (lambda (i) (nth i a)) indexes))

;; loop macro
(defun fetch-indexes (a &rest indexes)
  (loop :for i :in indexes
        :collect (nth i a)))

;; helper function
(defun fetch-indexes (a &rest indexes)
  (labels ((helper (indexes)
             (if (endp indexes)
                 '()
                 (cons (nth (first indexes) a)
                       (helper (rest indexes))))))
    (helper indexes)))

;; test (works the same with all)
(fetch-indexes '(a b c d) 2 3 0 1)
; ==> (c d a b)

应避免在递归中使用apply,但我会说明它是如何完成的。

(defun fetch-indexes (a &rest indexes)
  (if (endp indexes)
      '()
      (cons (nth (first indexes) a)
            (apply #'fetch-indexes a (rest indexes)))))

在您的示例中,您有嵌套列表。为了实现这一点,您还需要将其展平。我还没有这样做,所以这些支持一个适当的元素列表。