トラック
/
Elixir
Elixir
/
演習
/
スタックアンダーフロー
スタックアンダーフロー

スタックアンダーフロー

学習演習

はじめに

例外

Elixirのすべてのエラーは、_例外ビヘイビア_を実装しています。_アクセスビヘイビア_と同じように、_例外ビヘイビア_は、ビヘイビアというソフトウェア上の契約を満たすためにモジュールが実装しなければならないコールバック関数を定義します。エラーを定義すると、次のような性質を持つようになります:

  • モジュール名がエラーの名前になります。
  • モジュールがエラー構造体を定義します。
  • 構造体には:messageフィールドがあります。
  • raise/1とraise/2でそのモジュールを使い、目的のエラーを発生させることができます。

_例外ビヘイビア_は、message/1とexception/1という2つのコールバックも指定します。実装しなかった場合は、デフォルトの実装が使われます。message/1は、raiseから呼ばれると、エラー構造体を読みやすいメッセージに変換します。exception/1は、raise/2から呼ばれると、メッセージに追加の文脈を加えられるようにします。

例外を定義する

エラーモジュールから例外を定義するには、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_を実装する新しいエラーを2つ作ることにしました。

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. 割り算を行う関数を書く

スタック_(2つの数値の配列)_を引数に取る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を学んでマスターできます。すべて無料です。