按多个标准排序的斯卡拉成语 [英] Scala idiom for ordering by multiple criteria

查看:120
本文介绍了按多个标准排序的斯卡拉成语的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做这样的事情:

  class Foo extends Ordered [Foo] {
val x
val y
val z




def compare(that:Foo)= {
val c0 = this.length compareTo that.length //主要比较
lazy val c1 = this.x compareTo that.x //二次比较
lazy val c2 = this.y.size compareTo that.y.size //第三个比较
lazy val c3 = this.z.head compareTo that.z.head //最后的联系断言器$ b $如果(c0!= 0)c0 else if(c1!= 0)c1 else if(c2!= 0)c2 else if(c3!= 0)c3 else c4
}
}

我想知道是否有更清晰的方式来写这种东西。我期待有一些像 Ordering.multipleBy(排序:Ordered [A] *)签名,它采用可比较变量的可变参数并选择第一个非零。

解决方案

使用 Ordering 而不是 Ordered code>。 Ordering 是一个类型类,比 Ordered 更灵活(如果仅仅因为 Ordered 必须由要比较的类型实现,而 Ordering 可以在外部定义)。要为您的类型定义自然排序(默认 Ordering 实例),您只需在随播对象中定义一个隐式排序值。


$ b $因此,足够的序言。好的一点是,当使用 Ordering 时,你想要做的事很简单,因为对元组有一个隐式排序(假设元组本身有排序) :

  object Foo {
implicit val FooOrdering = Ordering.by {foo:Foo =>
(foo.length,foo.x,foo.y,foo.z)
}
}

另外,还有一个隐式转换,它将任何具有 Ordering 类型实例的值转换为有序的值(见 Ordered.orderingToOrdered ),所以我们没有什么特别的事情可以自动地传递任何 Foo 添加到期望 Ordered [Foo] 的函数中)




更新:关于您的新问题:


稍有关系 - 是否有任何订单制作的方式?

一种方法是使用基于 Ordering .by 和转换为元组,但显式传递顺序来组合:

  val byXOrdering = Ordering .by {foo:Foo => foo.x} 
val byYOrdering = Ordering.by {foo:Foo => foo.y}
val byZOrdering = Ordering.by {foo:Foo => foo.z}

//由XOrdering和ByYrdering组成:
val byXThenYOrdering = Ordering.by {foo:Foo => (foo,foo)}(Ordering.Tuple2(byXOrdering,byYrrdering))

//由XOrdering和ByYrdering和byZOrdering组成:
val byXThenYThenZOrdering = Ordering.by {foo:Foo => (foo,foo,foo)}(Ordering.Tuple3(byXOrdering,byYrrdering,byZrrdering))

但是它相对嘈杂。
我只用标准库找不到任何东西,所以我实际上建议使用我们自己的帮助器:

  final class CompositeOrdering [T](val ord1:Ordering [T],val ord2:Ordering [T])extends Ordering [T] {
def compare(x:T,y:T)= {
val comp = ord1.compare(x,y)
if(comp!= 0)comp else ord2.compare(x,y)
}
}
object CompositeOrdering {
def apply [T](orderings:Ordering [T] *)= orderings reduceLeft(_ orElse _)
}
implicit class OrderingOps [T](val ord:Ordering [T] )扩展AnyVal {
def orElse(ord2:Ordering [T])= new CompositeOrdering [T](ord,ord2)
}

可以这样使用:

  val byXOrdering = Ordering.by {foo:Foo => foo.x} 
val byYOrdering = Ordering.by {foo:Foo => foo.y}
val byZOrdering = Ordering.by {foo:Foo => foo.z}

//通过XOrdering和ByYrdering编写:
val byXThenYOrdering = byXOrdering orElse byYrrdering

//由XOrdering和ByYrdering和ByZrrdering组成:
val byXThenYThenZOrdering = byXOrdering orEsese byYrrdering orEsese byZrrdering

甚至更简单,如下所示:

  //编写byXOrdering和byYrrdering:
val byXThenYOrdering = CompositeOrdering(byXOrdering,byYrrdering)

//通过XOrdering和byYrrdering and byZOrdering:
val byXThenYThenZOrdering = CompositeOrdering(byXOrdering,byYrrdering,byZrrdering)

CompositeOrdering.apply 基本上就是您在问题中称为 Ordering.multipleBy 的部分。


I want to do something like this:

class Foo extends Ordered[Foo] {
   val x
   val y
   val z
   .
   .
   .
   .
   def compare(that: Foo) = {
      val c0 = this.length compareTo that.length          // primary comparison
      lazy val c1 = this.x compareTo that.x               // secondary comparison
      lazy val c2 = this.y.size compareTo that.y.size     // tertiary comparison
      lazy val c3 = this.z.head compareTo that.z.head     // final tie breaker
      if (c0 != 0) c0 else if (c1 != 0) c1 else if (c2 != 0) c2 else if (c3 != 0) c3 else c4
   }    
}

I was wondering if there was any cleaner way to write this kind of thing. I am expecting some thing like Ordering.multipleBy(ordering: Ordered[A]*) signature which takes a varargs of comparables and selects first non zero.

解决方案

It is often better to use Ordering instead of Ordered. Ordering is a type class and is much more flexible than Ordered (if only because Ordered must be implemented by the type to compare, while with Ordering you can define this outside). To define the natural ordering (default Ordering instance) for your type, you just define an implicit ordering value in the companion object.

So, enough with the preamble. The nice thing is that when using Ordering what you want to do is pretty simple, as there is an implicit ordering for tuples (provided that the tuple elements themselves have a orderings)`:

object Foo {
  implicit val FooOrdering = Ordering.by{ foo: Foo => 
    (foo.length, foo.x, foo.y, foo.z) 
  }
}

In addition, there is an implicit conversion that converts any value that has an Ordering type class instance into an Ordered value (see Ordered.orderingToOrdered) so we have nothing special to do to automagically being able to pass any instance of Foo to a function that expects an Ordered[Foo])


UPDATE: Concerning your new question:

Slightly related - is there any way to compose orderings?

One way to do it would be to use mostly the same technic based on Ordering.by and conversion to tuples, but explicitly passing the orderings to compose:

val byXOrdering = Ordering.by{ foo: Foo => foo.x }
val byYOrdering = Ordering.by{ foo: Foo => foo.y }
val byZOrdering = Ordering.by{ foo: Foo => foo.z }

// Compose byXOrdering and byYOrdering:
val byXThenYOrdering = Ordering.by{ foo: Foo => (foo, foo) }(Ordering.Tuple2(byXOrdering, byYOrdering))

// Compose byXOrdering and byYOrdering and byZOrdering:
val byXThenYThenZOrdering = Ordering.by{ foo: Foo => (foo, foo, foo) }(Ordering.Tuple3(byXOrdering, byYOrdering, byZOrdering))

But it is relatively "noisy". I could not find anything better using only the standard library, and so I would actually advise to use our own helper:

final class CompositeOrdering[T]( val ord1: Ordering[T], val ord2: Ordering[T] ) extends Ordering[T] {
  def compare( x: T, y: T ) = {
    val comp = ord1.compare( x, y )
    if ( comp != 0 ) comp else ord2.compare( x, y )
  }
}
object CompositeOrdering {
  def apply[T]( orderings: Ordering[T] * ) = orderings reduceLeft (_ orElse _)
}
implicit class OrderingOps[T]( val ord: Ordering[T] ) extends AnyVal {
  def orElse( ord2: Ordering[T] ) = new CompositeOrdering[T]( ord, ord2 )
}

Which can be used like this:

val byXOrdering = Ordering.by{ foo: Foo => foo.x }
val byYOrdering = Ordering.by{ foo: Foo => foo.y }
val byZOrdering = Ordering.by{ foo: Foo => foo.z }

// Compose byXOrdering and byYOrdering:
val byXThenYOrdering = byXOrdering orElse byYOrdering

// Compose byXOrdering and byYOrdering and byZOrdering:
val byXThenYThenZOrdering = byXOrdering orElse byYOrdering orElse byZOrdering

Or even simpler, like this:

// Compose byXOrdering and byYOrdering:
val byXThenYOrdering = CompositeOrdering(byXOrdering, byYOrdering)

// Compose byXOrdering and byYOrdering and byZOrdering:
val byXThenYThenZOrdering = CompositeOrdering(byXOrdering, byYOrdering, byZOrdering)

CompositeOrdering.apply is basically what you called Ordering.multipleBy in your question.

这篇关于按多个标准排序的斯卡拉成语的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆