簡體   English   中英

遞歸運行時實現Java與其他/功能語言?

[英]Recursion runtime implementation Java vs. other/functionals languages?

我喜歡遞歸,但在Java,你在某個時刻遇到了死胡同。 例如,我有一個大約100K迭代的遞歸不起作用的情況(StackOverflowError)。 糟糕的是,我不得不為這個運行時堆棧限制原因切換到惱人的“命令性循環”。

我想知道其他(特別是功能性)語言如何在運行時繞過堆棧溢出? 我想特別是函數式語言運行時更好地處理這個問題,因為遞歸是核心概念......

有人有一些信息或外部資源嗎?

大多數語言都有尾遞歸的編譯器優化。 尾遞歸意味着遞歸調用應該是遞歸方法的最后一次調用。 然后,編譯器可以將其優化為循環,從而防止發生堆棧溢出錯誤。

我不確定所有javac實現是否都實現了尾遞歸。 它不是規范要求的東西。 但是,對於任何遞歸程序來說,它都是一種重要的優化技術,所以我認為主流實現確實提供了尾遞歸。

您可以通過采用(簡單的)非尾遞歸程序自行測試,該程序生成StackOverflowError並使其尾遞歸(例如,計算階乘 )。

編輯 :之前有一個關於 Java中的尾遞歸問題 ,如用戶sje397的評論中所示。 還要看看Stephen C對這個問題的回答 ,它提供了一些額外的信息。

這是@Ronald Wildenberg回答的后續內容:

我不確定所有javac實現是否都實現了尾遞歸。 它不是規范要求的東西。

簡短的回答是他們不支持它。

更長的答案是,由於JVM設計,尾遞歸優化在Java中是一個棘手的問題。 閱讀John Rose @ Oracle撰寫的這篇博客文章 ,他談到了這個問題。 博客條目的主旨是提出字節碼擴展以支持“硬”尾調用。 但最后一段暗示為什么實現“軟”(即透明)尾調用很難。 尾調用優化會干擾JVM捕獲堆棧跟蹤的能力,這對Java安全體系結構具有“影響”。

Sun Bug數據庫條目提供了有關該問題的更多詳細信息。 閱讀評論。

似乎在當前JVM上實現尾遞歸的方法是在編譯器前端實現它。 顯然Scala做到了這一點。

“使用~100K迭代進行遞歸”是應該避免的,而不僅僅是Java。 它僅適用於尾部調用優化,但這並不總是可行的。 因此,最好不要首先養成過度遞歸的習慣。

遞歸是人們在第一次學習它們時往往會過度使用的概念之一,因為它似乎太酷了,不能到處展示......

正如其他人所提到的,支持正確的尾遞歸有幫助。 但是它本身並不足以支持深度遞歸。 盡管BLUB程序員可能會想到,深度遞歸自然適合某些任務,例如處理深度遞歸的數據結構。

支持深(非尾)遞歸的策略通常包含在支持一流延續的策略中。 您可能會發現有關First-Class Continuations的實施策略 (Clinger等人)很有意思。

我的頭腦中有一些策略:

  • CPS轉換您的程序或以其他方式堆分配延續幀(缺點:丟失堆棧的一些性能優勢)
  • 在堆棧溢出時,將堆棧復制到單獨的內存塊並將堆棧指針重置為基址; 在下溢時,將舊堆棧復制回來
  • 在堆棧溢出時,從堆中分配一個新的堆棧區域並在那里重置堆棧指針; 在下溢時,回到舊堆棧的末尾
  • 在堆棧溢出時,創建一個新線程繼續運行計算,並等待線程的結果(缺點:創建一個線程可能很昂貴(但工作線程可以被緩存),並且跨線程移動計算可能會導致havok with thread-本地存儲等)

您可以使用以下命令增加Java堆棧大小

java -Xss1024k MyProgram

(將java的堆棧大小增加到1024 KB。)但通常使用深度遞歸不是一個好主意。 嘗試制作迭代解決方案。

暫無
暫無

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

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