簡體   English   中英

實現尾部呼叫消除的一些好方法是什么?

[英]What are some good ways of implementing tail call elimination?

我用C / C ++的混合編寫了一個小的Scheme解釋器,但我還沒有實現正確的尾調用

我知道MTA算法上的經典切尼 ,但還有其他很好的實現方法嗎? 我知道我可以將Scheme堆棧放在堆上,但這仍然不能正確消除,因為標准表示應該支持無限數量的活動尾部調用。

我也擺弄了longjmps,但到目前為止,我認為它只適用於非相互遞歸尾調用。

基於C的主要方案如何實現正確的尾遞歸?

比編寫編譯器和VM更簡單的方法是注冊和蹦出您的解釋器。 既然你有一個解釋器而不是編譯器(我假設),你只需要幾個簡單的轉換來獲得對尾調用的適當支持。

你必須先用延續傳遞方式編寫所有內容,這在C / C ++中可能很奇怪。 Dan Friedman的ParentheC教程將指導您將高級遞歸程序轉換為可機器轉換為C的表單。

最后,你將實際上實現一個簡單的VM,而不是使用常規函數調用來執行eval,applyProc等,你通過設置全局變量傳遞參數,然后goto下一個參數(或使用top-級別循環和程序計數器)...

return applyProc(rator, rand)

reg_rator = rator
reg_rand = rand
reg_pc = applyProc
return

也就是說,通常以遞歸方式相互調用的所有函數都被簡化為偽程序集,在偽程序集中,它們只是不會重復出現的代碼塊。 頂級循環控制程序:

for(;;) {
  switch(reg_pc) {
    case EVAL:
      eval();
      break;
    case APPLY_PROC:
      applyProc();
      break;
    ...
  }
}

編輯:我使用JavaScript編寫了我的業余愛好Scheme解釋器的相同過程。 我利用了很多匿名程序,但它可能有助於作為一個具體的參考。 查看FoxScheme的提交歷史,從2011-03-13( 30707a0432563ce1632a )開始到2011-03-15( 5dd3b521dac582507086 )。

編輯^ 2:非尾遞歸仍會占用內存,即使它不在堆棧中。

在不知道你擁有什么的情況下,我會說最簡單(也是最有啟發性)的方法是從Dybvig的“Three Implementation Models for Scheme”中實現方案編譯器和VM。
我在這里用Javascript完成了(Dybvig的PDF副本也在那里): https//github.com/z5h/zb-lisp

檢查src / compiler.js:compileCons,並在src / vm.js中執行“操作碼”

如果您對口譯員的實施技術感興趣,那么Christian Queinnec的書“LiSP - Lisp in Small Pieces”是無法解決的。 它通過完整的代碼非常徹底地解釋了實現Scheme系統的所有方面。 這是一本很棒的書。

http://www.amazon.com/exec/obidos/ASIN/0521562473/qid=945541473/sr=1-2/002-2995245-1849825

但不要忘記查看ReadScheme.org上的論文。

這部分

編譯器技術/實現技術和優化http://library.readscheme.org/page8.html

有很多關於尾調用優化的論文。

除此之外,您還可以找到Dybvig論文(經典)的鏈接,該論文編寫得非常好。 它以非常清晰的方式解釋和激勵一切。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM