【问题标题】:Implicit ordering of case classes scala案例类scala的隐式排序
【发布时间】:2014-07-23 17:37:24
【问题描述】:

案例类在 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 限制它的情况下,您也不知道第一个元素是什么类型,因此您必须找到某种方法来比较任意对象与另一个任意对象
  • @MarioCamou 很好的隐含你可以覆盖,这只是我想要的一堆 1-time 用例类
  • 去年我就这个问题写了a blog post。不过值得注意的是,非通用解决方案 (implicit def OrderingA: Ordering[A] = Ordering.by(_.i)) 一点也不差。
  • @TravisBrown 只是在寻找一种简单的方法使所有案例类排序,如果您有一个不涉及使用外部库的方法,您也可以发布答案

标签: scala implicit case-class shapeless


【解决方案1】:

案例类的排序可以使用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 版本的一部分。

【讨论】:

  • 如有疑问,请使用 shapeless。
  • 我不是在抱怨,但从技术上讲,您应该明确说明您提供的链接何时是宣传您自己的产品。无论如何,这个框架看起来很酷,它是否允许像 c++11 中的可变参数样式模板
  • 什么?没有ProductTypeClass? :)
  • @aaronman 我从未见过将这些披露限制应用于 FOSS 项目......无论如何,很明显我是它的作者之一。回答你的问题 wrt ariadic style templates ...是的。
  • @TravisBrown 嗯……你说得有道理。但是,我们刚刚获得了 HListCoproduct 的原始 Ordering/PartialOrdering 实例,无论出于何种原因,我都没有想到要问这个问题。
【解决方案2】:

为了完整起见,我将成为 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 为您提供了一个用于此类通用编程的更有用的受限工具包。

【讨论】:

  • 当我第一次看到宏时,它们似乎有点没用,准引号(无论它们看起来多么危险)似乎你实际上可以完成一些事情
  • 是的,与手动构建 AST 相比,准引号是一个巨大的改进,而且它们不再危险(尽管我同意字符串中代码的整个想法一开始有点笨拙)。跨度>
猜你喜欢
  • 2013-04-13
  • 1970-01-01
  • 2014-11-02
  • 2020-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 2017-03-29
相关资源
最近更新 更多