その結果、少しに分かりにくいところを解説しておく。
次のようなNodeの定義を考える。
trait Node {
def play(move: Move): Option[Node]
def possibleMoves(m: Marker): Seq[Move]
def isTerminal: Boolean
}
探索は、この3つのメッソドとあとは適切なscore関数があれば可能である。
そして、このNodeを実装したReversiNodeを定義することを考える。ReversiNodeは当然、この3つのメソッドに対する実装を与える必要がある。さらに、ReversiNodeにはNodeにはない、winnerメソッドなどが含まれる。
class ReversiNode extends Node {
override def play(move: Move): Option[Node] = ...
override def possibleMoves(m: Marker): Seq[Move] = ...
override def isTerminal: Boolean = ...
def winner: Marker = ...
}
ここで、問題となるのはplayの返り値の型である。このままでは、playから帰ってきたオブジェクトに対してwinnerメソッドなどReversiNode特有のメソッドを呼び出せない。
val n2 = n1.play(move) n2.winner // Error!
Javaであれば、こういうときにはcastを使うことになるが、動作時にエラーとなる危険がある。
ReversiNode n2 = (ReversiNode) n1.play(move); n2.winner();
Scalaでは、型パラメータを用いることでcastを使わずに安全なコードを作成することができる。
まず、Nodeの宣言を次のように修正する。
trait Node[Repr <: Node[Repr]] {
def play(move: Move): Option[Repr]
def possibleMoves(m: Marker): Seq[Move]
def isTerminal: Boolean
}
この[Repr <: Node[Repr]]というのは、
1. Reprという型変数を用いる(playの返り値に用いている)、
2. ReprはNode[Repr]のサブクラスである、という意味である。
そして、実装クラスであるReversiNodeを次のようにする。
class ReversiNode extends Node[ReversiNode] {
override def play(move: Move): Option[ReversiNode] = ...
override def possibleMoves(m: Marker): Seq[Move] = ...
override def isTerminal: Boolean = ...
def winner: Marker = ...
}
これで、playの返り値がサブクラスのReversiNodeであることが保証された。
最後に、Nodeを使うクラスを作る場合は、Nodeが型パラメータを必要とするために、次のように用いる。
trait Player[N <: Node[N]] {
def play(node: N, last: Move): Move
}
0 件のコメント:
コメントを投稿