- 一種特殊的遞迴結構 - 函數的最後一個操作是對自身的呼叫 - Ruby, Javascript並沒有針對此優化 ### factorial example ```ruby= def factorial(n, acc = 1) return acc if n <= 1 factorial(n - 1, n * acc) end puts factorial(5) # => 120 ``` - 在這個函數中,參數 acc 是一個累加器,用於存儲到目前為止的計算結果。 - 遞迴呼叫 factorial(n - 1, n * acc) 是函數的最後一個操作 - n是變數,當大於1時會不斷呼叫遞迴,並且將n減去1。 - 形成一個尾遞迴。 - 當n等於1時,返回acc。 ### 優化 Tail Call Optimization, TCO 指的是編譯器支援TCO - 重用堆疊空間 - 提高遞迴的效能 - 減少記憶體使用 可以在Ruby中啟用TCO優化 ```ruby= RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true, trace_instruction: false } =begin 這段程式碼會設定Ruby VM的編譯選項,讓其開啟TCO。 但要注意的是,TCO優化可能會使程式的偵錯變得更困難,因為它會修改呼叫堆疊。 此外,不是所有的Ruby實作或版本都支援TCO優化,因此在實際使用時需要特別小心。 =end ``` ### 不優化會產生的問題 - 遞迴呼叫會在記憶體堆疊,用於保存該次呼叫的變數和狀態。 - 如果遞迴深度非常大,那麼將會有大量的記憶體被佔用,可能導致stack overflow > 當遞迴深度達到一定程度時,系統將無法繼續執行,將異常終止。 ### 那Javascript有TCO優化嗎? - ECMAScript 2015(ES6)的版本中,有TCO優化 - 大部分主流的JavaScript環境(包括瀏覽器和Node.js)還未全面覆蓋。 - 只有在一些特定的瀏覽器,例如在Safari Chrome因為debug困難而移除了TCO支援 https://chromestatus.com/feature/5516876633341952 ### Conclusion 如果需要處理大量遞迴,可能還需要考慮使用其他解決方案,例如將遞迴轉換為迴圈,或者使用尾遞迴與異步(例如Promises或async/await)的結合方式來避免stack overflow。