我有以下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))
。
有没有办法在没有这个问题的情况下通过我的列表?
答案 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
配对的访问者函数,为您提供第一个元素和列表的其余部分。 rest
与cdr
相同。
&rest
是一个lambda list keyword,它会在其后面的变量名中包含其余参数。
你真的在寻找apply
。想象一下,我创建了一个可以接受0个或更多数字参数并将它们加在一起的函数:
(defun add (&rest numbers)
(apply #'+ numbers))
Apply可以使用两个以上的参数。第一个是调用函数,除了最后一个函数之外的所有函数都是放在最后一个arguments元素前面的额外参数。保证实现支持50个参数或者函数可以采用的参数数量,特定实现支持50以上。
(apply #'+ 1 2 '(3 4 5)) ; ==> 15
现在由&rest
和apply
递归会产生效率低下的代码,因此您应该使用更高阶函数,循环宏或帮助:
;; 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)))))
在您的示例中,您有嵌套列表。为了实现这一点,您还需要将其展平。我还没有这样做,所以这些支持一个适当的元素列表。