Optimization commonlisp中一个有效的collect函数

Optimization commonlisp中一个有效的collect函数,optimization,recursion,lisp,common-lisp,Optimization,Recursion,Lisp,Common Lisp,我正在学习Lisp,并编写了以下函数来收集结果列表 (defun collect (func args num) (if (= 0 num) () (cons (apply func args) (collect func args (- num 1))))) 它产生了与内置循环函数类似的输出 CL-USER> (collect #'random '(5) 10) (4 0 3 0 1 4 2 1 0 0) CL-USER> (loop

我正在学习Lisp,并编写了以下函数来收集结果列表

(defun collect (func args num)
  (if (= 0 num)
      ()
      (cons (apply func args)
        (collect func args (- num 1)))))
它产生了与内置循环函数类似的输出

CL-USER> (collect #'random '(5) 10)
(4 0 3 0 1 4 2 1 0 0)
CL-USER> (loop repeat 10 collect (random 5))
(3 3 4 0 3 2 4 0 0 0)
然而,当我试图生成一个100000个元素长的列表时,我的collect函数破坏了堆栈

CL-USER> (length (collect #'random '(5) 100000))
Control stack guard page temporarily disabled: proceed with caution
而循环版本没有

CL-USER> (length (loop repeat 100000 collect (random 5)))
100000

我如何使我的版本更节省空间,除了考虑,还有其他选择吗?我认为是尾部递归。我用的是sbcl。任何帮助都会很好。

好吧,递归的替代方法是迭代

您应该知道,与Scheme不同,CommonLisp不需要来自其实现者的尾部递归


不,它不是尾部递归的。ANSI Common Lisp既没有提到它,也没有提到您的代码:

(defun collect (func args num)
  (if (= 0 num)
      ()
      (cons (apply func args)
            (collect func args (- num 1)))))
如果你看一下你的代码,就会发现你的付费电话周围有一个缺点。此CONS接收要收集的递归调用的值。所以COLLECT不能是尾部递归的。通过引入累加器变量,将函数重写为类似尾部递归的函数相对简单。各种Lisp或Scheme文献都应该对此进行描述

在通用Lisp中,编程迭代计算的默认方法是使用以下几种迭代构造之一:DO、DOTIME、DOLIST、LOOP、MAP、MAPCAR

通用Lisp标准不提供尾部调用优化(TCO)。必须指定TCO在存在其他几种语言功能的情况下应该做什么。例如,动态绑定和特殊变量对TCO有影响。但是通用的Lisp标准对总体TCO和TCO可能产生的影响只字不提。TCO不是ANSI通用Lisp标准的一部分

几种常见的Lisp实现都有一种方法可以通过编译器开关实现各种尾部调用优化。请注意,启用这些功能的方法和限制都是特定于实现的

摘要:在常见的Lisp中,使用迭代构造,而不是递归

(defun collect (func args num)
  (loop repeat num
        collect (apply #'func args)))

额外的好处:它更易于阅读。

ANSI标准不要求通用Lisp实现来进行尾部调用优化;然而,大多数值得他们盐(包括SBCL)做优化

另一方面,您的函数不是尾部递归的。可以使用引入额外参数以累积中间结果的常见技巧将其转换为一个:

(defun collect (func args num) (labels ((frob (n acc) (if (= 0 n) acc (frob (1- n) (cons (apply func args) acc))))) (frob num nil))) (defun collect(func args num) (标签)(frob(n acc) (如果(=0 n) 行政协调会 (frob(1-n)(cons(apply func args)acc()()))) (frob num nil))) (原始参数FUNC和ARGS在局部函数中被删除,因为它们与recpect一致)

(defun collect (func args num) (labels ((frob (n acc) (if (= 0 n) acc (frob (1- n) (cons (apply func args) acc))))) (frob num nil)))