-1

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

ECLバイトコードについてはほとんど知りません。末尾再帰の最適化はないようです。専門家はそれを説明できますか?

心から!

4

2 に答える 2

3

ECLのインタプリタは現在、末尾呼び出しの最適化を行いません。簡単に実装できますが、実行する時間がありません。基本的には、末尾呼び出しを通知するためにバイトコードコンパイラに1つのフラグを追加することになります。いずれにせよ、ここで指摘されているように、ECLインタープリターは、動的に割り当てられたスタックと、インタープリターの再帰のためのCスタックを使用します。これは、環境を追跡するために、約1000個のCスタックフレーム(小さい)といくつかのconsedリストを取得することを意味します。現在はそれで十分ですが、大丈夫です。ただし、C側では、ECLは自己末尾呼び出しを検出し、それらの多くを最適化できます。その他の場合、GCCは相互末尾呼び出し(末尾位置にある他の関数への呼び出し)を最適化します。

于 2012-09-25T14:17:49.457 に答える
0

ECLについては何も知りませんが、コンパイルしたソースコードから見て、後で逆アセンブリすると、コンパイラは正しく機能しました。関数は、それ自体への再帰呼び出しとして定義されます。分解で見たのと同じです。したがって、この関数の呼び出し中に発生する可能性がある唯一の問題は、スタックオーバーフローと算術オーバーフローです。

于 2012-09-19T01:22:21.550 に答える