订购,订购和比较选项

时间:2011-09-29 16:03:31

标签: scala

假设:

case class Person(name: String)

并尝试做:

scala> List(Person("Tom"), Person("Bob")).sorted

导致对丢失订购的投诉。

<console>:8: error: could not find implicit value for parameter ord: Ordering[Person]
   List(Person("Tom"), Person("Bob")).sorted

但是这个:

case class Person(name: String) extends Ordered[Person] {
  def compare(that: Person) = this.name compare that.name }

按预期正常工作:

scala> List(Person("Tom"), Person("Bob")).sorted
res12: List[Person] = List(Person(Bob), Person(Tom))

虽然没有涉及订购或暗示。

问题#1:这里发生了什么? (我的钱是隐含的......)

然而,考虑到上述事实,这个:

scala> Person("Tom") > Person("Bob")
res15: Boolean = true

有效,也是这样:

scala> List(Some(2), None, Some(1)).sorted

开箱即用:

res13: List[Option[Int]] = List(None, Some(1), Some(2))

我希望如此:

scala> Some(2) > Some(1)

也可以,但不会:

<console>:6: error: value > is not a member of Some[Int]
       Some(2) > Some(1)

问题#2:为什么不呢,我怎样才能让它发挥作用?

6 个答案:

答案 0 :(得分:26)

如果您安装稍微过于神奇的默认范围内的奖励,则可以比较以下选项:

scala> import scala.math.Ordering.Implicits._
import scala.math.Ordering.Implicits._

scala> def cmpSome[T: Ordering](x: Option[T], y: Option[T]) = x < y
cmpSome: [T](x: Option[T], y: Option[T])(implicit evidence$1: Ordering[T])Boolean

导入为您提供了一个隐含的从订购到具有中缀操作的类,因此它足以让Ordering不再导入。

答案 1 :(得分:10)

关于您的第一个问题:Ordered[T]延伸Comparable[T]Ordering随播广告对象为可以转换为Ordering[T]的任何值提供隐式Comparable[T]

implicit def ordered[A <% Comparable[A]]: Ordering[A]

没有隐式转化A : Ordering => Ordered[A] - 这就是Some(1) > Some(2)无效的原因。

定义这样的转换是否是个好主意,因为您可能最终将对象包装到Ordered个实例中,然后再次创建Ordering个实例(依此类推...... )。更糟糕的是:您可以在范围内创建两个Ordered个实例,但这些实例当然不是您想要的。

答案 2 :(得分:2)

List的sorted方法的定义是:

def sorted [B >: A] (implicit ord: Ordering[B]): List[A]

所以是的,隐式事情正在发生,但标准库中的许多类都有与之关联的隐式对象,而不必先导入它们。

Ordering伴侣对象定义了一堆隐式排序。其中包括OptionOrdering和IntOrdering,它有助于解释列表调用sorted的能力。

要获得在可用隐式转换时使用运算符的功能,您需要导入该对象,例如:

def cmpSome(l:Option[Int], r:Option[Int])(implicit ord:Ordering[Option[Int]]) = {
  import ord._
  l < r
}

scala> cmpSome(Some(0), Some(1))
res2: Boolean = true

答案 3 :(得分:2)

要回答第二个问题,为什么不能这样做:Some(2) > Some(1)

您可以导入并使用Option[Int]而不是Some[Int]

@ import scala.math.Ordering.Implicits._ 
import scala.math.Ordering.Implicits._
@ Some(2) > Some(1) // doesn't work
cmd11.sc:1: value > is not a member of Some[Int]
val res11 = Some(2) > Some(1)
                    ^
Compilation Failed
@ (Some(2): Option[Int]) > (Some(1): Option[Int]) // Option[Int] works fine
res11: Boolean = true
@ Option(2) > Option(1) 
res12: Boolean = true
@ (None: Option[Int]) > (Some(1): Option[Int]) 
res13: Boolean = false

在实践中,您的类型可能是Option[Int]而不是Some[Int],所以它不会那么难看,您也不需要明确的向上转换。

答案 4 :(得分:0)

我假设你理解为什么当你没有传递一个Ordering并且在范围内没有可用时,sort不起作用。 至于为什么在从Ordered trait扩展类时,排序函数的工作原理。答案是当你从Ordered trait扩展时,代码类型检查trait包含的函数如&lt;,&gt;因此,没有必要进行隐式转换,因此没有抱怨缺少隐式订购。

关于你的第二个问题,Some(2) > Some(1)将不起作用,因为有些不扩展特征Ordered,在范围内似乎没有任何隐式函数隐式地将Some转换为具有函数{{ 1}}

答案 5 :(得分:0)

感谢您提供带有示例的详细问题。

我的答案基于我从这里的一篇出色文章中学到的知识:http://like-a-boss.net/2012/07/30/ordering-and-ordered-in-scala.html

在此归功于作者。

引用该文章:

Coming back to our Box example - the scala library defines an implicit conversion between Ordered[T] and Ordering[T] and vice-versa.

https://github.com/scala/scala/blob/2.12.x/src/library/scala/math/Ordered.scalaOrdered的伴随对象在此处提供了所需的转换:

/** Lens from `Ordering[T]` to `Ordered[T]` */ implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] = new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }

但是反向转换没有定义,我不确定为什么吗?