4

定义了扩展 Ordering[A] 的类 A 和 A 的子类 B,如何自动对 B 数组进行排序?Scala 编译器抱怨它“找不到参数 ord: Ordering[B] 的隐含值”。这是一个具体的 REPL 示例(Scala 2.8),其中 A = Score 和 B = CommentedScore:

class Score(val value: Double) extends Ordered[Score] {
  def compare(that: Score) = value.compare(that.value)
}
defined class Score

trait Comment { def comment: String }
defined trait Comment

class CommentedScore(value: Double, val comment: String) extends Score(value) with Comment
defined class CommentedScore

val s = new CommentedScore(10,"great")
s: CommentedScore = CommentedScore@842f23

val t = new CommentedScore(0,"mediocre")
t: CommentedScore = CommentedScore@dc2bbe

val commentedScores = Array(s,t)
commentedScores: Array[CommentedScore] = Array(CommentedScore@b3f01d, CommentedScore@4f3c89)

util.Sorting.quickSort(commentedScores)
error: could not find implicit value for parameter ord: Ordering[CommentedScore]
       util.Sorting.quickSort(commentedScores)
                             ^

我该如何解决这个问题(也就是说,“免费”对 Array[B] = Array[CommentedScore] 进行排序,因为我知道如何对 Array[A] = Array[Score] 进行排序),以一种避免样板的优雅方式?

谢谢!

4

3 回答 3

3

自己添加所需的隐式:

implicit val csOrd: Ordering[CommentedScore] = Ordering.by(_.value)

您可以将其放在CommentedScore伴随对象中,以便在使用站点没有样板。


编辑:如果您希望仅在继承树的顶部定义排序方法,您仍然必须Ordering为每个子类提供一个,但您可以根据对象中的一个定义compare方法。IEOrderingScore

object Score {
  implicit val ord: Ordering[Score] = Ordering.by(_.value)
}

object CommentedScore {
  implicit val csOrd = new Ordering[CommentedScore] { 
    def compare(x: CommentedScore, y: CommentedScore) = Score.ord.compare(x, y)
  }
}

如果您不想为每个子类重新定义它,您可以使用通用方法来生成Ordering

object Score {
  implicit def ord[T <: Score]: Ordering[T] = Ordering.by(_.value)
}

这效率有点低,因为它是 adef而不是 a ,每次需要时val它都会创建一个新的。Ordering然而,开销可能很小。还要注意,现在我们有了s就不需要Orderedtrait 和method 了。compareOrdering

于 2012-08-29T05:29:24.880 回答
2

您可以使用Orderfrom scalaz,它是逆变的,因此您不需要为每个子类定义它。这是一个例子:

import scalaz._
import Scalaz._

class Score(val value: Double)
object Score {
  implicit val scoreOrd: Order[Score] = orderBy(_.value)
}
trait Comment { def comment: String }
class CommentedScore(value: Double, val comment: String) extends Score(value) with Comment {
  override def toString = s"cs($value, $comment)"
} 
def quickSort[E: Order](list: List[E]): List[E] = list match {
  case Nil => Nil
  case head :: tail =>
    val (less, more) = tail partition { e => implicitly[Order[E]].order(e, head) == LT }
    quickSort(less) ::: head :: quickSort(more) 
}
println(quickSort(List(
  new CommentedScore(10,"great"),
  new CommentedScore(5,"ok"),
  new CommentedScore(8,"nice"),
  new CommentedScore(0,"mediocre")
))) // List(cs(0.0, mediocre), cs(5.0, ok), cs(8.0, nice), cs(10.0, great))
于 2012-08-29T07:54:32.910 回答
1

这有效:

val scoreArray: Array[Score] = Array(s, t)
util.Sorting.quickSort(scoreArray)

或者,如果您从以下位置开始Array[CommentedScore]

val scoreArray: Array[Score] = commentedScores.map(identity)
util.Sorting.quickSort(scoreArray)

请注意,您可以使用以下命令进行更简单的排序:

scoreArray.sorted
于 2012-08-29T05:14:31.323 回答