案例类在scala中似乎没有隐式排序。
scala> case class A(i:Int)
defined class A
scala> implicitly[Ordering[A]]
<console>:10: error: No implicit Ordering defined for A.
implicitly[Ordering[A]]
我想知道是否总是为所有案例类定义一个隐式排序,如果不是,至少有一种方法可以为相同成员类型的案例类/案例类的每个arity定义隐式排序。 / p>
答案 0 :(得分:8)
案例类的排序可以使用shapeless,
自动导出import GenericOrdering._
case class Foo(i : Int, s : String)
implicitly[Ordering[Foo]]
val fs = List(
Foo(2, "b"),
Foo(2, "a"),
Foo(1, "c")
).sorted
assert(fs == List(
Foo(1, "c"),
Foo(2, "a"),
Foo(2, "b")
))
有关完整示例,请参阅here。完整的机制和PartialOrdering
的扩展将成为即将发布的2.1.0版本的一部分。
答案 1 :(得分:3)
为了完整起见,我将成为Miles良好解决方案的坏天使。实际上,您可以使用宏轻松地滚动自己的此功能版本:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
object OrderingHelper {
implicit def apply[A]: Ordering[A] = macro apply_impl[A]
def apply_impl[A: c.WeakTypeTag](c: Context) = {
import c.universe._
val A = weakTypeOf[A]
val fields = A.decls.collect {
case sym: MethodSymbol if sym.isCaseAccessor => q"a.${sym.name}"
}
if (fields.isEmpty) c.abort(c.enclosingPosition, "Not a case class!") else
A.baseClasses.collectFirst {
case sym
if sym.name.decodedName.toString.startsWith("Tuple")
&& sym.owner == typeOf[Any].typeSymbol.owner =>
c.abort(c.enclosingPosition, "Not needed for tuples!")
} getOrElse c.Expr[Ordering[A]](q"Ordering.by((a: $A) => (..$fields))")
}
}
然后:
scala> import OrderingHelper._
import OrderingHelper._
scala> case class B(i: Int, s: String)
defined class B
scala> Ordering[B]
res0: scala.math.Ordering[B] = scala.math.Ordering$$anon$9@2c9df057
scala> Ordering[B].compare(B(1, "foo"), B(1, "bar"))
res1: Int = 1
上面的代码将在REPL中工作,2.11中没有额外的依赖项,对于2.10,您只需要一些小的调整和一个编译器插件(详见my blog post)。
我绝对推荐使用Shapeless方法,但是 - Shapeless为这种通用编程提供了一个更有用的约束工具包。