如何对sealed trait进行排序?

6
我有一个由“状态机”(“流程图”)定义的分布式系统。
每个系统将其状态写入共享“日志”。
我将每个状态表示为密封特质的一部分以及该状态的给定“状态”。
我希望“合并/减少”到单个状态,以表示当前进度。
(有一些放宽措施,因为不是所有人都必须成功才能最终成功完成状态)
有两个密封特质代表流程:
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
}
4个回答

3
你可以定义一个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,但我个人不喜欢在compare方法中使用扩展的case objects,而每个case object中使用样板式的compare更加糟糕。


如果我有一系列的对象,我不明白现在该如何使用它。 - Avba

2

一种方法是通过定义几个Map来确定你对SystemStatus的优先级,然后通过Ordering.by定义(System, Status)的顺序:


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(优先级最低)。根据需要,它们可以设置为任何其他值。
要减少(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)

1
考虑使用枚举类型 CatsOrderValueEnum 来定义顺序,详见 enumeratum-cats {{link1}}。
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

where

libraryDependencies ++= Seq(
  "com.beachape" %% "enumeratum" % "1.5.13",
  "com.beachape" %% "enumeratum-cats" % "1.5.15"
)

0

一旦你为每个case object分配了一个整数,你就可以通过减去它们来编写自己的Ordering#compare方法:

sealed trait System
case object A extends System
case object B extends System
case object C extends System
case object D extends System
object System {
  implicit val ordering: Ordering[System] = new Ordering[System] {
    def compare(x: System, y: System): Int = {
      def id(value: System): Int = value match {
        case A => 0
        case B => 1
        case C => 2
        case D => 3
      }

      id(x) - id(y)
    }
  }
}

sealed trait Status
case object Pending extends Status
case object InProgress extends Status
case object Success extends Status
case object Fail extends Status

object Status {
  implicit val ordering: Ordering[Status] = new Ordering[Status] {
    def compare(x: Status, y: Status): Int = {
      def id(value: Status): Int = value match {
        case Pending => 0
        case InProgress => 1
        case Success => 2
        case Fail => 3
      }

      id(x) - id(y)
    }
  }
}

然后你可以轻松地对序列进行排序:

val sorted = Seq((D, Fail), (D,Pending), (A,Fail), (D,InProgress)).sorted
// val sorted = List((A, Fail), (D, Pending), (D, InProgress), (D, Fail))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接