【问题标题】:How to do ordering of a sealed trait?如何对密封特征进行排序?
【发布时间】:2019-09-04 01:25:09
【问题描述】:

我有一个由某种“状态机”(“流程图”)定义的分布式系统

每个系统都在共享的“日志”中写入状态

我将每个状态表示为密封特征的一部分以及该状态的给定“状态”

我想“合并/减少”到代表当前进度的单个状态。

(有一些放宽,因为并非所有都必须成功才能成功完成最终状态)

有 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
}

【问题讨论】:

    标签: scala traits enumeration comparable partial-ordering


    【解决方案1】:

    你可以定义一个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 对象将被&lt; 和朋友丰富。

    也可以让你的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(以及样板文件compare 在每种情况下的替代对象都是偶数更糟)。

    【讨论】:

    • 如果我有一系列对象,我不明白我现在如何使用它
    【解决方案2】:

    一种方法是在几个Maps 中定义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(最低优先级)。可以根据需要将它们设置为任何其他值。

    要减少Seq(System, Status)s:

    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)
    

    【讨论】:

      【解决方案3】:

      考虑 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"
      )
      

      【讨论】:

        猜你喜欢
        • 2016-03-27
        • 1970-01-01
        • 1970-01-01
        • 2015-05-26
        • 1970-01-01
        • 1970-01-01
        • 2016-07-18
        • 2015-04-22
        • 2011-09-29
        相关资源
        最近更新 更多