學習軌道
/
Elixir
Elixir
/
練習
/
堆疊下溢
堆疊下溢

堆疊下溢

學習練習

簡介

例外

Elixir 中的所有錯誤都實作了_例外行為_。就像_存取行為_一樣,_例外行為_定義了模組必須實作的幾個回呼函式,以滿足這個行為的軟體契約。一旦定義了錯誤,它就具備下列特性:

  • 模組的名稱定義了錯誤的名稱。
  • 模組定義了一個錯誤結構。
  • 這個結構會有一個:message欄位。
  • 這個模組可以和raise/1與raise/2搭配使用,來拋出預期的錯誤

_例外行為_也指定了兩個回呼函式:message/1 和 exception/1。如果沒有實作,就會使用預設的實作。當搭配raise呼叫時,message/1會把錯誤結構轉換成可讀的訊息。當搭配raise/2呼叫時,exception/1允許在訊息中加入額外的脈絡資訊。

定義例外

若要從錯誤模組定義例外,我們會使用defexception巨集:

# Defines a minimal error, with the name `MyError`
defmodule MyError do
  defexception message: "error"
end

# Defines an error with a customized exception/1 function
defmodule MyCustomizedError do
  defexception message: "custom error"

  @impl true
  def exception(value) do
    case value do
      [] ->
        %MyCustomizedError{}

      _ ->
        %MyCustomizedError{message: "Alert: " <> value}
    end
  end
end

使用例外

定義好的錯誤可以像內建錯誤一樣,透過raise/1或raise/2來使用。

  • raise/1會依照模組名稱拋出特定的錯誤;或者,如果引數是字串,就會拋出一個RuntimeError,並以該字串做為訊息。
  • raise/2會依照模組名稱拋出特定的錯誤,並接受一個屬性引數,用來取得帶有適當訊息的錯誤。

說明

在你繼續於 Instruments of Texas 工作的同時,RPN 計算機的 Elixir 實作也有了進展。你的團隊希望能引發比標準函式庫提供的通用錯誤更具體的錯誤。你正在做一些研究,但已經決定要實作兩個新的錯誤,這兩個錯誤會實作 Exception Behaviour。

1. 除以零的錯誤

將一個數字除以零會產生未定義的結果,團隊認為用錯誤來表示最為合適。

實作DivisionByZeroError模組,讓它的錯誤訊息為:"division by zero occurred"

raise DivisionByZeroError
# => ** (DivisionByZeroError) division by zero occurred

2. 遇到堆疊下溢時的錯誤

RPN 計算機使用_堆疊_來追蹤尚未相加的數字。團隊用數字(整數和浮點數)的陣列來表示這個_堆疊_,例如:[3, 4.0]。每個運算需要在堆疊上有特定數量的數字,才能執行計算。當堆疊上的數字不夠時,就稱為堆疊下溢錯誤。實作StackUnderflowError例外,它提供一則預設訊息,以及可選的額外情境資訊。

raise StackUnderflowError
# => ** (StackUnderflowError) stack underflow occurred

raise StackUnderflowError, "when dividing"
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing

3. 撰寫除法函式

實作divide/1函式,它接收一個堆疊_(兩個數字組成的陣列)_,並且:

  • 當堆疊沒有包含足夠的數字時,引發_堆疊下溢_
  • 當除數為 0 時,引發_除以零_(注意,堆疊中的數字是以相反的順序儲存的)
  • 當沒有引發任何錯誤時,執行除法
RPNCalculator.Exception.divide([])
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing

RPNCalculator.Exception.divide([0, 100])
# => ** (DivisionByZeroError) division by zero occurred

RPNCalculator.Exception.divide([4, 16])
# => 4

注意,陣列的順序是相反的!

透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Elixir Exercism

準備好開始 堆疊下溢 了嗎?

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