所以我过去一个月一直在学习Scala,昨天我用简单的排序算法在REPL中玩。使用标准的函数快速排序,很容易在元素类型中创建一个多态的排序函数,但它让我思考:如果我可以使它在数据结构类型中也具有多态性呢?为List
,Vector
等提供单独的功能令人讨厌。当然,我可以只用一个Seq[E]
,但似乎无法保证Seq[E]
的底层实现。所有线性集合都实现Seq
,它具有我们需要的所有功能(filter
,apply
和append
),因此我们应该能够拥有一个可以排序的函数所有线性集合,对吗?我想出了以下代码:
object QSort {
def qsort[E, D[E] <: Seq[E]](s: D[E])(c: (E, E) => Int): D[E] = {
if (s.length <= 1) s
else {
val pivot: E = s(s.length / 2)
qsort[E, D](s.filter((x: E) => c(x, pivot) < 0))(c)
.append(s.filter((x: E) => c(x, pivot) == 0))
.append(qsort[E, D](s.filter((x: E) => c(x, pivot) > 0))(c))
}
}
}
它采用一些D[E]
的子类作为Seq[E]
的子类和比较器函数,并且应该应用效率极低的快速排序。但是,编译器说当我调用filter
时,类型不匹配,因为过滤器返回Seq[E]
而不是D[E]
。为什么filter
不返回D[E]
,这种多态性是否真的可能?
答案 0 :(得分:1)
我非常推荐阅读更多有关CanBuildFrom
的内容,但以下是如何将其用于您的问题:
scala> :pa
// Entering paste mode (ctrl-D to finish)
import scala.collection.generic.CanBuildFrom
object QSort {
def qsort[E, D[E] <: Seq[E]]
(s: D[E])(c: (E, E) => Int)
(implicit cbf: CanBuildFrom[D[E], E, D[E]]): D[E] =
{
if (s.size <= 1)
s
else
{
val pivot: E = s(s.size / 2)
(qsort(s.filter((x: E) => c(x, pivot) < 0))(c) ++
s.filter((x: E) => c(x, pivot) == 0) ++
qsort(s.filter((x: E) => c(x, pivot) > 0))(c)).to[D]
}
}
}
// Exiting paste mode, now interpreting.
import scala.collection.generic.CanBuildFrom
defined module QSort
scala> val l = List(1, -10, 12, 2)
l: List[Int] = List(1, -10, 12, 2)
scala> val c = (a: Int, b:Int) => if (a == b) 0 else if (a < b) 1 else -1
c: (Int, Int) => Int = <function2>
scala> QSort.qsort(l)(c)
res0: List[Int] = List(12, 2, 1, -10)
scala> QSort.qsort(l.toSeq)(c)
res1: scala.collection.immutable.Seq[Int] = List(12, 2, 1, -10)
scala> QSort.qsort(l.toVector)(c)
res2: Vector[Int] = Vector(12, 2, 1, -10)
正如您所看到的,我传递给qsort
的任何类型都与我回来的类型相同。