如果A
具有Ordered[A]
特征,我希望能够拥有像这样的代码
val collection: List[List[A]] = ... // construct a list of lists of As
val sorted = collection sort { _ < _ }
获取列表按字典顺序排序的内容。当然,仅仅因为A
具有特征Ordered[A]
并不意味着List[A]
具有特征Ordered[List[A]]
。然而,据推测,执行此操作的“scala方式”是隐式def。
如何将List[A]
隐式转换为Ordered[List[A]]
,假设A具有特征Ordered[A]
(以便上面的代码正常工作)? < / p>
我记得在List[A]
个对象上使用词典排序,但我想要的代码可以适应其他顺序。
答案 0 :(得分:19)
受Ben Lings的回答启发,我设法找出了一种似乎最简单的按字典顺序对列表进行排序的方法:添加行
import scala.math.Ordering.Implicits._
在进行List [Int]比较之前,确保隐式函数infixOrderingOps
在范围内。
答案 1 :(得分:5)
(11分钟前我实际上不知道该怎么做,我希望能回答我自己的问题。)
implicit def List2OrderedList[A <% Ordered[A]](list1: List[A]): Ordered[List[A]] = {
new Ordered[List[A]] {
def compare(list2: List[A]): Int = {
for((x,y) <- list1 zip list2) {
val c = x compare y
if(c != 0) return c
}
return list1.size - list2.size
}
}
}
这里需要注意的重要一点是“view bound”A <% Ordered[A]
,它确保A
Ordered[A]
不需要Predef
,只是有办法做这个转换。令人高兴的是,Scala库的对象Int
具有从RichInt
到Ordered[Int]
的隐式转换,特别是{{1}} s。
其余代码只是实现了词典排序。
答案 2 :(得分:4)
灵感来自Ben Lings的回答,我写了我自己的sort
版本:
def sort[A : Ordering](coll: Seq[Iterable[A]]) = coll.sorted
相当于:
def sort[A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A]) = coll.sorted
请注意,ordering
会隐式转换为Ordering[Iterable[A]]
。
示例:
scala> def sort[A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A]) = coll.sorted
sort: [A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A])Seq[Iterable[A]]
scala> val coll = List(List(1, 3), List(1, 2), List(0), Nil, List(2))
coll: List[List[Int]] = List(List(1, 3), List(1, 2), List(0), List(), List(2))
scala> sort(coll)
res1: Seq[Iterable[Int]] = List(List(), List(0), List(1, 2), List(1, 3), List(2))
有人询问如何提供自己的比较功能;只需使用Ordering.fromLessThan:
即可scala> sort(coll)(Ordering.fromLessThan(_ > _))
res4: Seq[Iterable[Int]] = List(List(), List(2), List(1, 3), List(1, 2), List(0))
Ordering.by
允许您将值映射到已有Ordering实例的另一种类型。鉴于元组也是有序的,这对于案例类的字典比较非常有用。
举一个例子,让我们定义一个Int的包装器,应用Ordering.by(_.v)
,其中_.v
提取基础值,并显示我们得到相同的结果:
scala> case class Wrap(v: Int)
defined class Wrap
scala> val coll2 = coll.map(_.map(Wrap(_)))
coll2: List[List[Wrap]] = List(List(Wrap(1), Wrap(3)), List(Wrap(1), Wrap(2)), List(Wrap(0)), List(), List(Wrap(2)))
scala> sort(coll2)(Ordering.by(_.v))
res6: Seq[Iterable[Wrap]] = List(List(), List(Wrap(0)), List(Wrap(1), Wrap(2)), List(Wrap(1), Wrap(3)), List(Wrap(2)))
最后,让我们对具有更多成员的案例类做同样的事情,重用元组的比较器:
scala> case class MyPair(a: Int, b: Int)
defined class MyPair
scala> val coll3 = coll.map(_.map(MyPair(_, 0)))
coll3: List[List[MyPair]] = List(List(MyPair(1,0), MyPair(3,0)), List(MyPair(1,0), MyPair(2,0)), List(MyPair(0,0)), List(), List(MyPair(2,0)))
scala> sort(coll3)(Ordering.by(x => (x.a, x.b)))
res7: Seq[Iterable[MyPair]] = List(List(), List(MyPair(0,0)), List(MyPair(1,0), MyPair(2,0)), List(MyPair(1,0), MyPair(3,0)), List(MyPair(2,0)))
答案 3 :(得分:3)
在2.8中,您应该能够collection.sorted
。 sorted
采用隐式Ordering
参数。任何实现Ordered
的类型都有相应的Ordering
(感谢隐式转换Ordering.ordered
)。如果Ordering.Iterable
有Iterable[T]
,还有隐式Ordering
会使T
拥有Ordering
。
但是,如果您尝试这样做则不起作用:
scala> def sort[A <: Ordered[A]](coll: List[List[A]]) = coll.sorted
<console>:5: error: could not find implicit value for parameter ord: Ordering[List[A]]
def sort[A <: Ordered[A]](coll: List[List[A]]) = coll.sorted
^
您需要明确指定您想要Ordering[Iterable[A]]
:
def sort[A <: Ordered[A]](coll: List[List[A]]) = coll.sorted[Iterable[A]]
如果集合的元素类型为Ordering[Iterable[A]]
,我不确定为什么编译器找不到List[A]
。
答案 4 :(得分:2)
受Daniel的评论启发,这是一个递归版本:
implicit def toOrdered[A <% Ordered[A]](list1: List[A]): Ordered[List[A]] = {
@scala.annotation.tailrec
def c(list1:List[A], list2:List[A]): Int = {
(list1, list2) match {
case (Nil, Nil) => 0
case (x::xs, Nil) => 1
case (Nil, y::ys) => -1
case (x::xs, y::ys) => (x compare y) match {
case 0 => c(xs, ys)
case i => i
}
}
}
new Ordered[List[A]] {
def compare(list2: List[A]): Int = c(list1, list2)
}
}
关于评论: 我曾经认为这更像是一种品味问题。有时在递归函数上验证正确性更容易,当然你的版本足够短,没有令人信服的理由更喜欢递归。
但我对性能的影响很感兴趣。所以我尝试对它进行基准测试:见http://gist.github.com/468435。我很惊讶地看到递归版本更快(假设我正确地做了基准测试)。对于长度为10的列表,结果仍然适用。
答案 5 :(得分:0)
由于我已经用另一种方法实现了,所以这里是一个不使用return
的非递归版本:
new Ordering[Seq[String]]() {
override def compare(x: Seq[String], y: Seq[String]): Int = {
x.zip(y).foldLeft(None: Option[Int]){ case (r, (v, w)) =>
if(r.isDefined){
r
} else {
val comp = v.compareTo(w)
if(comp == 0) None
else Some(comp)
}
}.getOrElse(x.size.compareTo(y.size))
}
}