我有一个由某种“状态机”(“流程图”)定义的分布式系统
每个系统都在共享的“日志”中写入状态
我将每个州表示为密封特征和该州的给定“状态”
我想“合并/减少”到一个代表当前进度的状态。
(有一些放松,因为并非所有条件都必须成功才能使最终状态成功完成)
有2个密封的特性代表流量:
sealed trait System
case object A extends System
case object B extends System
case object C extends System
...
sealed trait Status
case object Pending extends Status
case object InProgress extends Status
case object Success extends Status
case object Fail extends Status
日志:
A, Success
B, Fail
C, Pending
...
...
现在有一组规则可用于定义单个状态减少
基本上,它具有优先级
A < B < C, ... < Z
和
Pending < InProgress < Success < Fail
因此,如果状态为:
(A, Success)
与(C, Pending)
我想将其减少为(C,Pending)
以及
(A,Success)
与(B, Fail)
我想将其减少为(B, Fail)
在我的情况下,我可以将其建模为简单的整数比较(可能使用我明确测试过的异常值)
我不清楚如何使密封的特征可比/有序,这将使我的生活更轻松
符合以下条件的事情就足够了:
def reduce(states: Seq[(System,Status)]) : (System,Status) = {
states.order... {left.system < right.system) && (a.status < b.status) ... possibly another ordering test ....}.tail // take the last one in the ordering
}
答案 0 :(得分:2)
您可以定义scala.math.Ordering[Status]
:
object StatusOrdering extends Ordering[Status] {
def compare(x: Status, y: Status): Int =
(x, y) match {
// assuming that the ordering is Pending < InProgress < Success < Fail...
case (_, _) if (x eq y) => 0
case (Pending, _) => -1
case (_, Pending) => 1
case (InProgress, _) => -1
case (_, InProgress) => 1
case (Success, _) => -1
case (_, Success) => 1
case _ => 0 // (Fail, Fail)
}
然后您可以在reduce
中
import StatusOrdering.mkOrderingOps
,您的Status
对象将与<
和朋友一起充实。
也可以将trait
扩展Ordered[Status]
,以定义特征中的规范顺序:
sealed trait OrderedStatus extends Ordered[OrderedStatus] {
def compare(that: OrderedStatus): Int =
(this, that) match {
case (x, y) if (x eq y) => 0
case (Qux, _) => -1
case (_, Qux) => 1
case (Quux, _) => -1
case (_, Quux) => 1
case _ => 0
}
}
case object Qux extends OrderedStatus
case object Quux extends OrderedStatus
case object Quuux extends OrderedStatus
然后,您不必导入mkOrderingOps
,但是我个人不喜欢case objects
方法中扩展compare
的向前使用(以及样板{{1 }}在每种情况下,对象甚至更糟。
答案 1 :(得分:1)
一种方法是在两个System
中定义Status
和Map
的优先级,然后通过(System, Status)
定义Ordering.by
的顺序:
val syMap: Map[System, Int] = Map(A->1, B->2, C->3)
val stMap: Map[Status, Int] = Map(Pending->1, InProgress->2, Success->3, Fail->4)
implicit val ssOrdering: Ordering[(System, Status)] =
Ordering.by{ case (sy, st) => (syMap.getOrElse(sy, 0), stMap.getOrElse(st, 0)) }
import ssOrdering._
(A, Success) < (C, Pending)
// res1: Boolean = true
(A, Success) < (B, Fail)
// res2: Boolean = true
(C, Pending) < (B, Fail)
// res3: Boolean = false
请注意,在上面的示例代码中,不匹配的System/Status
的默认值被设置为0
(最低优先级)。可以根据需要将它们设置为任何其他值。
要减少Seq
中的(System, Status)
:
def ssReduce(ss: Seq[(System, Status)])(implicit ssOrd: Ordering[(System, Status)]) : (System, Status) = {
import ssOrd._
ss.reduce((acc, t) => if (t < acc) acc else t ) // Or simply `ss.max`
}
ssReduce(Seq((A, Success), (C, Pending), (B, Fail)))
// res4: (System, Status) = (C,Pending)
答案 2 :(得分:1)
考虑使用enumeratum-cats CatsOrderValueEnum
方法来定义订单
import cats.Order
import enumeratum.values._
import cats.instances.int._
sealed abstract class Status(val value: Int) extends IntEnumEntry
object Status extends CatsOrderValueEnum[Int, Status] with IntEnum[Status] {
case object Pending extends Status(1)
case object InProgress extends Status(2)
case object Success extends Status(3)
case object Fail extends Status(4)
val values = findValues
}
object AdtOrder extends App {
import Status._
println(Order[Status].compare(Pending, Fail))
}
输出
-1
其中
libraryDependencies ++= Seq(
"com.beachape" %% "enumeratum" % "1.5.13",
"com.beachape" %% "enumeratum-cats" % "1.5.15"
)