學習軌道
/
Elixir
Elixir
/
練習
/
RPN 計算機
RPN 計算機

RPN 計算機

學習練習

簡介

錯誤

錯誤難免會發生。在 Elixir 裡,雖然大家常說「讓它崩潰就好」,但有時候我們仍需要把函式呼叫救回已知的良好狀態,以滿足軟體合約。在某些語言裡,錯誤被當成控制流程的一種手段,但在 Elixir 裡並不鼓勵這種模式。我們通常光看名稱就能認出哪些函式可能會拋出錯誤:會拋出錯誤的函式,名稱結尾帶有!。這是相對於會回傳{:ok, value}或:error的函式。看看這些函式庫範例:

Map.fetch(%{a: 1}, :b)
# => :error
Map.fetch!(%{a: 1}, :b)
# => raises KeyError

Try/Rescue

Elixir 提供了一種用try .. rescue從錯誤中救回的建構。

try do
  raise RuntimeError, "error"
rescue
  e in RuntimeError -> :error
end

我們來看看這個建構:

  • 第 1 行,用try宣告這個程式區塊。
  • 第 2 行,這裡放的是可能出錯的函式呼叫,在這個例子中我們呼叫的是raise/2。
  • 第 4 行,在rescue區段裡,我們對所拋出錯誤的_模組_名稱做模式配對
    • 在->的左側:
      • e 會配對到錯誤結構。
      • in 是關鍵字。
      • RuntimeError 是我們想救回的錯誤。
        • 如果我們想從所有錯誤中救回,可以用_代替模組名稱,或完全省略in這個關鍵字。
    • 在右側:
      • 當錯誤相符時要執行的指令。

錯誤結構

用這種方式救回的錯誤(有時也稱為「例外」)是結構。 在 Elixir 裡,救回錯誤的情況非常少見。 通常救回的錯誤會被記錄下來,或送到外部的監控服務,然後再重新拋出。 這表示我們通常不太在意特定錯誤結構的內部結構。

在例外概念中,你會學到更多關於錯誤結構的內容,包括如何定義自己的自訂錯誤。

說明

在 Instruments of Texas 工作時,你的任務是開發一個以 Elixir 撰寫的實驗性逆波蘭表示法 [RPN] 計算機。你的團隊遇到了某些操作會引發錯誤、導致行程崩潰的問題。你的任務是撰寫一個函式來包裝操作函式,讓錯誤能用更優雅、更符合 Elixir 慣用寫法的方式處理。

1. 警告團隊

實作函式calculate!/2,以堆疊作為唯一的引數來呼叫操作函式。這個操作函式定義在別處,但你知道它可能順利完成,也可能引發錯誤。

stack = []
operation = fn _ -> :ok end
RPNCalculator.calculate!(stack, operation)
# => :ok

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate!(stack, operation)
# => ** (ArgumentError) An error occurred

函式名稱以!結尾,是在警告程式設計師這個函式可能會引發錯誤

2. 包裝錯誤

在做更多研究時,你注意到許多函式會用原子和元組來表示成功或失敗。請用這個策略來實作calculate/2。

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate(stack, operation)
# => :error

3. 傳遞訊息

有些錯誤含有重要資訊,你的同事必須知道這些資訊,才能確保系統正確運作。實作calculate_verbose/2來傳遞錯誤訊息。這個錯誤是一個 struct,其中帶有:message欄位。

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:error, "An error occurred"}
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Elixir Exercism

準備好開始 RPN 計算機 了嗎?

註冊 Exercism,透過 58 個概念168 個練習 和真人引導來學習並精通 Elixir,全部免費。