我尝试在Scala中实现一个StateMachine,但是我遇到了一个关于类型系统的问题,这让我相当困惑。在下面的代码中,我需要让guard函数接受StateMachine的预期子类的参数。不幸的是,由于FunctionN参数的类型参数是逆变的,我不确定如何完成此操作。
class Transition[S, +M <: StateMachine[S]](start: S, end :S, var guard: Option[M => Boolean]) { // COMPILER ERROR ABOVE LINE : ^^ covariant type M occurs in contravariant position in type => Option[M => Boolean] of method guard ^^ val startState = start val endState = end def willFollow(stateMachine: M, withGuard : Boolean) = // COMPILER ERROR ABOVE : ^^ covariant type M occurs in contravariant position in type M of value stateMachine ^^ if (!withGuard && guard == None) 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); private var activeStates = initialStates def act() = { var entryStates = Set[S]() var exitStates = Set[S]() stateDrains.foreach {drain => val (exitState, transitionsOut) = drain // Follow non-epsilon transitions transitionsOut.filter(_.willFollow(this, true)).foreach {transition => exitStates += transition.startState entryStates += transition.endState } } // For all exit states we map the state to a set of transitions, and all sets are "flattened" into one big set of transitions // which will then filter by those which do not have a guard (epsilon transitions). The resulting filtered list of transitions // all contain endStates that we will map to. All of those end states are appended to the current set of entry states. entryStates = entryStates ++ (exitStates.flatMap(stateDrains(_)).filter(_.willFollow(this, false)).map(_.endState)) // Exclude only exit states which we have not re-entered // and then include newly entered states activeStates = ((activeStates -- (exitStates -- entryStates)) ++ entryStates) } override def toString = activeStates.toString } object HvacState extends Enumeration { type HvacState = Value val aircon, heater, fan = Value } import HvacState._ object 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](heater, fan, Some(_.temperature > 50)) } import HvacTransitions._ class HVac extends StateMachine[HvacState](Set(autoFan, turnOffAc, AcToHeater, HeaterToAc, HeaterToFan), Set(heater)) { var temperature = 40 }
您的转换仅适用于特定类型的状态,但也适用于特定类型的状态机,因此两个类型参数S
和M
。例如,在最后,您的转换可能取决于温度,这是StateMachine的一个属性,而不仅仅是状态。
不知何故,状态机应该只有与之兼容的转换。不应允许在没有温度的状态机上进行需要访问温度的转换。类型系统将强制执行。但是,您的代码没有为此做任何规定。
相反,StateMachine类获得一组转换[S,StateMachine[S]]。这是可行的,但结果是StateMachine只接受“标准”转换,而不需要机器的任何特殊功能。您可以定义需要特殊机器(带温度)的转换,但没有任何东西让机器接受这些特殊转换,即使它们与它兼容。
您需要做的是让类StandardMachine接受特殊的转换,它现在拒绝这种转换,但当然只接受与机器兼容的转换(如果您不给出这种保证,编译器将拒绝代码)。可能更简单的方法是将类型M也放入机器中,这样您就可以正确地表达约束。
这里有一个可能的方法。首先,我们向StateMachine添加一个类型参数
class StateMachine[S, M](
我们需要在引用StateMachine的任何地方添加M参数,例如类Transition[S,M<:StateMachine[S,M]]
或类Hvac extends StateMachine[HvacState,Hvac]
当然,构造函数参数变成
class StateMachine[S,M](transitions: Set[Transition[S, M]]], ...
在这里,我们声明机器的转换是正常的。只是我们没有。它仍然不编译,每次我们传递this
时,机器就会过渡,例如:
transitionsOut.filter(_.willFollow(this, true)).foreach {transition =>
^^
type mismatch; found : StateMachine.this.type (with underlying type StateMachine[S,M]) required: M
我们引入了类型M,但我们不是向机器传递一些M,而是传递this
。这是一个状态机器[S,M],它不需要是M,我们当然希望M是机器的类型,但不需要是这样的。我们不想说StateMachine[S,M] 必须是M。我们用self类型来做这件事:
class StateMachine[S, M](
transitions: Set[Transition[S, M]],
initialStates: Set[S]) { this: M =>
// body of the class
这充分利用了有问题的类型系统,但隔离机器状态可能更简单,也就是说,不使用self类型This:M=>
,而是使用一些defmachineState:M
,并将其传递给守卫,而不是This
。在这种情况下,HVAC
将是状态机[HvacState,Double]
(或者比Double更显式的温度封装),
我得更改摘要:
>
转换:移除M上的约束,移除协方差:
类转换[S,M](...
EpsilonTransition:删除对M的约束
类EpsilonTransition[S,M]
TurnofFAcc
:您复制的代码中缺少运算符,添加了<
本文向大家介绍JavaScript函数参数的传递方式详解,包括了JavaScript函数参数的传递方式详解的使用技巧和注意事项,需要的朋友参考一下 JavaScript使用一个变量对象来追踪变量的生存期。基本类型值被直接保存在变量对象内;而引用类型值则作为一个指针保存在变量对象内,该指针指向实际对象在内存中的存储位置。 基本类型值的传递 向参数传递基本类型值时,被传递的值会被复制给一个局部变量(即
本文向大家介绍PHP函数参数传递的方式整理,包括了PHP函数参数传递的方式整理的使用技巧和注意事项,需要的朋友参考一下 在调用函数时,需要向函数传递参数,被传入函数的参数称为实参,而函数定义的参数称为形参。而向函数传递参数的方式有四种,分别是值传递、引用传递、默认参数和可变长度参数。 1. 值传递 值传递是 PHP 中函数的默认传值方式,也称为“拷贝传值”。顾名思义值传递的方式会将实参的值复制一份
看两个例子: a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun(a) print a # [1] 所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。 通过id来看引用a的内存地址可以比较理解: a = 1 def fun(a): prin
将函数传递给另一个函数的Scala示例缺少传递的函数(时间段)接受参数(x)的情况。 我怎样才能使上述代码工作? 编辑:我在oncepersecond中添加了一个x,以明确目标是传递整数。
问题内容: 我很好奇Go中是否有可能。我有多种方法的类型。是否可以有一个函数,该函数需要一个方法参数,然后将其称为类型? 这是我想要的一个小例子: Go认为type 有一个称为的方法,而不是用传入的方法名称替换它。 问题答案: 是的,有可能。您有2(3)个选项: 规范:方法表达式 该表达式产生的功能与第一个参数等效,但具有一个显式接收器。它有签名。 在这里,方法接收器是显式的。您只需将方法名称(具
本文向大家介绍浅谈Python中函数的参数传递,包括了浅谈Python中函数的参数传递的使用技巧和注意事项,需要的朋友参考一下 1.普通的参数传递 2.参数个数可选,参数有默认值的传递 参数sep的缺省值是'_' 如果这个参数不给定值就会使用缺省值 如果给定 则使用给定的值 需要注意 如果一个参数是可选参数 那么它后面所有的参数都应该是可选的,另外 可选参数的顺序颠倒依然可以正确的给对应的参数赋值