Scalaには列挙型としてEnumerationが用意されているが、以下の理由で使いにくい。
object DNA extends Enumeration {
val A, C, G, T, N = Value
}
val base : DNA.Value = DNA.A
ここでDNA.Value型を拡張することが許されていないので、ラベルとしての機能しか持たせることができない。
ScalaではJavaのコードが使えるので、Javaのenumを使うのが簡便だが、Scalaのコードだけで同様の機能を実装するには、objectを使うと良い。
DNAの塩基を表すコード。genome-weaverのDNA.scalaより抜粋。
object DNA {
// objectで定義するとsingletonになる
case object A extends DNA(0)
case object C extends DNA(1)
case object G extends DNA(2)
case object T extends DNA(3)
case object N extends DNA(4)
// DNAの文字列をすべて並べる。
val values = Array(A, C, G, T, N)
// 用途によって別の集合を定義することもできる
val exceptN = Array(A, C, G, T)
private val codeTable = Array(A, C, G, T, N, N, N, N)
def complement(code:Int) : DNA = codeTable((~code & 0x03) | (code & 0x04))
}
// sealedを付けると、DNAを拡張したクラスはこのファイル内でしか定義できない
// abstractを付けると、DNAを拡張したクラスはA, C, G, T, N以外にないことを保証できるので
// match文がexhaustive(すべてのケースを網羅)になる
sealed abstrat class DNA(val code:Int) {
// A, C, G, T, Nをcase objectとすると、クラス名を表示するtoStringが実装される
val name = toString
// DNAクラスには自由にメソッドを定義できる
def complement = DNA.complement(code)
}
このように定義すると、パターンマッチが問題なく使えるし、complementなど機能を充実させることもできる。
val l : DNA = DNA.G
l match {
case DNA.A => ...
case DNA.C => ...
case DNA.G => ...
case DNA.T => ...
case DNA.N => ...
}