Scala ひと巡り : ケースクラス (Case Classes)

Scala はケースクラスの概念をサポートします。ケースクラスは通常のクラスであり、そのコンストラクタ・パラメータをエクスポートし、パターンマッチング [11]を介して再帰的な分解メカニズムを提供します。

次は、1 つの抽象スーパークラス Term と 3 つの具象ケースクラス Var、Fun と App からなるクラス階層の例です。

abstract class Term case class Var(name: String) extends Term case class Fun(arg: String, body: Term) extends Term case class App(f: Term, v: Term) extends Term

このクラス階層は、型付けされていない(untyped) λ計算 [54]の項を表現するのに使えます。ケースクラスのインスタンス構築にあたり、Scala では new プリミティブを使う必要がありません。単純に、クラス名を関数として使用できます。

次は 1 つの例です:

Fun("x", Fun("y", App(Var("x"), Var("y"))))

ケースクラスのコンストラクタ・パラメータは公開の値として扱われ、直接アクセスできます。

val x = Var("x") Console.println(x.name)

すべてのケースクラスに対して、Scala コンパイラは、構造的等価性を実装する equals メソッドと toString メソッドを生成します。たとえば:

val x1 = Var("x") val x2 = Var("x") val y1 = Var("y") println("" + x1 + " == " + x2 + " => " + (x1 == x2)) println("" + x1 + " == " + y1 + " => " + (x1 == y1))

は、次のように印字するでしょう。

Var(x) == Var(x) => true Var(x) == Var(y) => false

もしパターンマッチングをデータ構造の分解に使うなら、ケースクラスを定義するのが妥当です。次のオブジェクトは、λ計算を表現するプリティプリンタ関数を定義します:

object TermTest extends Application { def printTerm(term: Term) { term match { case Var(n) => print(n) case Fun(x, b) => print("^" + x + ".") printTerm(b) case App(f, v) => Console.print("(") printTerm(f) print(" ") printTerm(v) print(")") } } def isIdentityFun(term: Term): Boolean = term match { case Fun(x, Var(y)) if x == y => true case _ => false } val id = Fun("x", Var("x")) val t = Fun("x", Fun("y", App(Var("x"), Var("y")))) printTerm(t) println println(isIdentityFun(id)) println(isIdentityFun(t)) }

この例で関数 print は、パターンマッチング文として表現されており、それは match キーワードで始まる case Pattern => Body 節の並びから成っています。

上記のプログラムは、与えられた項が単純な識別関数に対応するかどうかチェックする関数 isIdentityFun も定義します。この例は、深いパターンとガードを使います。与えられた値をもつパターンとマッチした後、その(キーワード if の後に定義された)ガードが評価されます。もしそれが true を返すなら、マッチは成功です。;そうでなければ失敗であり、次のパターンが試みられます。