軌道
/
Gleam
Gleam
/
課程大綱
/
尾呼叫最佳化
尾呼

尾呼叫最佳化 在 Gleam

2 個練習

關於 尾呼叫最佳化

每次呼叫函式時,記憶體中都會建立一個新的堆疊框架,用來儲存該函式的引數與區域變數;當函式回傳時,這個堆疊框架就會被釋放。由於 Gleam 使用遞迴函式呼叫,而不是迴圈語法,這可能會讓這些堆疊框架佔用大量記憶體。

為了避免這個問題,Gleam 支援尾端呼叫最佳化,讓編譯器在函式呼叫是函式最後執行的動作時,重複使用目前函式的堆疊框架。這表示函式可以無限次地呼叫自己,而不會用到任何額外的記憶體。

未經最佳化的遞迴函式,通常可以透過「累加器」改寫成尾端呼叫最佳化的版本。

累加器是伴隨資料一起傳遞的變數。它用來保存函式目前的執行狀態,直到抵達基礎情況為止。

累加器應該由函式的作者初始化,而不是函式的使用者。要做到這點,可以宣告兩個函式:一個公開函式,只接收必要的資料作為引數並初始化累加器;以及一個私有函式,它也接收一個累加器。

// Count the length of a list without tail call optimisation
pub fn count(list: List(String)) -> Int {
  case list {
    [] -> 0
    [_, ..rest] -> {
      let amount = count(rest) // Non-tail recursive call
      amount + 1
    }
  }
}
// Count the length of a list with tail call optimisation
pub fn count(list: List(String)) -> Int {
  count_elements(list, 0)
}

fn count_elements(list: List(String), accumulator: Int) -> Int {
  case list {
    [] -> accumulator
    [_, ..rest] -> {
      let accumulator = accumulator + 1
      count_elements(rest, accumulator) // Tail recursive call
    }
  }
}
透過 GitHub 編輯 連結會在新視窗或分頁中開啟

學習 尾呼叫最佳化

練習已鎖定

再解鎖 2 個練習,就能練習 尾呼叫最佳化