その結果、少しに分かりにくいところを解説しておく。
次のような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 件のコメント:
コメントを投稿