私は Scala で StateMachine を実装しようとしましたが、型システムで問題が発生し、かなり困惑しました。以下のコードでは、ガード関数が StateMachine の予想されるサブクラスの引数を受け入れるようにする必要があります。残念ながら、FunctionN引数の型パラメーターは反変であるため、これをどのようにやってのけるかはわかりません。
class Transition[S, +M <: StateMachine[S]](start: S, end :S, var guard: Option[M => Boolean]) {
// 行の上のコンパイラ エラー : ^^ 共変型 M がメソッド ガードの型 => オプション[M => Boolean] の反変位置で発生します ^^
val startState = 開始
val endState = 終了
def willFollow(stateMachine: M, withGuard : Boolean) =
// 上記のコンパイラ エラー : ^^ 共変の型 M が値 stateMachine の型 M の反変の位置にあります ^^
if (!withGuard && ガード == なし) true;
else (withGuard && guard.get(stateMachine))
}
class EpsilonTransition[S, M <: StateMachine[S]](start: S,end :S) extends Transition[S, M](start, end, None)
class StateMachine[S](transitions: Set[Transition[S, StateMachine[S]]], initialStates: Set[S]) {
private val stateDrains = transitions.groupBy(_.startState);
プライベート var activeStates = initialStates
デフ行為()= {
var entryStates = Set[S]()
var exitStates = Set[S]()
stateDrains.foreach {ドレイン =>
val (exitState, transitionsOut) = ドレーン
// 非イプシロン遷移に従います
transitionsOut.filter(_.willFollow(this, true)).foreach {transition =>
exitStates += transition.startState
entryStates += transition.endState
}
}
// すべての終了状態について、状態を遷移のセットにマップし、すべてのセットを 1 つの大きな遷移セットに「フラット化」します
// 次に、ガードを持たないもの (イプシロン遷移) でフィルタリングします。トランジションのフィルタリングされた結果のリスト
// すべてにマップ先の endStates が含まれています。これらの終了状態はすべて、現在の開始状態のセットに追加されます。
entryStates = entryStates ++ (exitStates.flatMap(stateDrains(_)).filter(_.willFollow(this, false)).map(_.endState))
// 再入力していない終了状態のみを除外します
// そして、新しく入力された状態を含めます
activeStates = ((activeStates -- (exitStates -- entryStates)) ++ entryStates)
}
オーバーライド定義 toString = activeStates.toString
}
object HvacState extends Enumeration {
タイプ HvacState = 値
val エアコン、ヒーター、ファン = 値
}
HvacState をインポートします。_
オブジェクトHvacTransitions {
val autoFan = new EpsilonTransition[HvacState, HVac](aircon, fan)
val turnOffAc = new Transition[HvacState, HVac](aircon, fan, Some(_.temperature 75))
val HeaterToFan = new Transition[HvacState,HVac](ヒーター, ファン, Some(_.temperature > 50))
}
インポート HvacTransitions._
class HVac extends StateMachine[HvacState](Set(autoFan, turnOffAc, AcToHeater, HeaterToAc, HeaterToFan), Set(heater)) {
var 温度 = 40
}