Common lisp 为什么ECL可以计算阶乘(1000)?

Common lisp 为什么ECL可以计算阶乘(1000)?,common-lisp,ecl,Common Lisp,Ecl,ECL能计算fac(1000)真是太棒了!ECL如何做到这一点 >(defun fac (n) (if (= n 1) 1 (* n (fac (- n 1))))) >(disassemble #'fac) #(FAC N = - * #<bytecompiled-function FAC> SI:FSET) Name: FAC

ECL能计算fac(1000)真是太棒了!ECL如何做到这一点

 >(defun fac (n) (if (= n 1) 1 (* n (fac (- n 1)))))
 >(disassemble #'fac)
 #(FAC N = - * #<bytecompiled-function FAC> SI:FSET)
 Name:           FAC                                                                 
    0    POP     REQ
    1    BIND    N
    3    NOMORE
    4    PUSHV   0
    6    PUSH    1
    8    CALLG   2,=
   11    JNIL    18
   13    QUOTE   1
   15    SET     VALUES(0),REG0
   16    JMP     35
   18    PUSHV   0
   20    PUSHV   0
   22    PUSH    1
   24    CALLG   2,-
   27    PUSH    VALUES(0)
   28    CALLG   1,FAC
   31    PUSH    VALUES(0)
   32    CALLG   2,*
   35    EXIT
>(定义fac(n)(如果(=n1)1(*n(fac(-n1‘)!)
>(拆卸#'fac)
#(FAC N=-*#SI:FSET)
姓名:FAC
0 POP请求
1绑定N
3诺莫尔
4伏0
6推1
8呼叫2=
11 JNIL 18
13引文1
15个设定值(0),REG0
16 JMP 35
18伏0
20伏0
22推1
24呼叫2-
27推送值(0)
28呼叫1,外交部
31推送值(0)
32呼叫2*
35出口
我对ECL字节码知之甚少。似乎没有尾部递归优化。有专家能解释一下吗


真诚的

我对ECL一无所知,但从您编译的源代码以及后来在dis-assembly中看到,编译器工作正常。该函数被定义为对自身的递归调用。我在dis-assembly中也看到了同样的情况。因此,调用此函数期间可能出现的唯一问题是堆栈溢出和算术溢出

ECL的解释器目前没有进行尾部调用优化。它可以很容易地实现,但我没有时间这么做:基本上,它相当于向字节码编译器添加一个标志来表示尾部调用。在任何情况下,正如这里指出的,ECL解释器使用动态分配的堆栈,加上用于解释器递归的C堆栈。这意味着您将获得大约1000个C堆栈帧(小)和一些用于跟踪环境的消耗列表。目前已经足够了,这是可以的。不过,在C端,ECL确实检测到自尾调用,并且可以优化其中的许多调用,在其他情况下,GCC优化相互尾调用(对处于尾位置的其他函数的调用)。

这是字节码,但可能解释器可以进行优化?1000层的堆栈并不是一个真正的问题——解释器实现应该已经解决了这个问题(如果它真的对这个问题进行递归的话)<代码>(定义fac(n)(减少#'*(i从1到n的循环收集i))甚至计算(fac 30000)或更多。太棒了,谢谢!如果解释器真的保留了一个堆栈,它将被实现为数据结构,像堆栈一样运行,因此它可以任意运行多个级别的堆栈(不确定内部实现,但它可能会施加限制,或者限制是系统的限制)。我在函数中没有看到任何尾部递归(
#'*
处于尾部位置),因此尾部调用优化与此无关。使用newlisp计算fac(30)时会出现溢出,因此我真正想知道的是如何(E)CL可以在不溢出的情况下完成。顺便说一句,它会打印出结果的所有2568位数字吗?@z_轴会自动从fixnum升级到bignum,因为reult溢出了fixnum的大小。原则上相对简单(但让bignum数学快速可能有点棘手)。