2

我试图定义一个函数来检查通用 Seq 是否已排序。

我想出了这个:

import Ordering.Implicits._

def isOrdered[A: Ordering](seq: Seq[A]): Boolean =
  seq.sliding(2).map({ case List(a, b) => b > a }).forall(identity)

此时,编译器会返回“没有为 A 定义隐式排序”。

我可以通过按如下方式分配 a 和 b 来解决此问题:

def isOrdered[A: Ordering](seq: Seq[A]): Boolean =
  seq.sliding(2).map({ case List(a: A, b: A) => a < b }).forall(identity)

此时编译器会愉快地接受该函数。

我觉得好奇的是,以下实现是开箱即用的:

def isOrdered[A: Ordering](seq: Seq[A]): Boolean =
  seq.sliding(2).exists{s => s(0) > s(1)}

据我所知,唯一显着的区别是我不使用偏函数。

谁能解释这种行为?

4

2 回答 2

1

接受的答案对我来说非常不满意,因为我仍然不知道发生了什么。

我的第一反应是,它一定是隐含+推理的错误,无论他们是否知道。

问题不在于,例如,类型参数 for map,因为它减少到匹配:

scala> def f[X: Ordering](seq: Seq[X]) = seq match { case List(a,b) => b > a }

这怎么可能行不通?我的猜测是它与 Ordered 的不变性有关,因此将 List.unapply 解包的方式与输入 Seq 进行比较意味着我们不能依赖范围内的隐式 Ordering。

让我们打开一些调试。(-Xprint:typer、patmat、-Xlog-implicits、-Yinfer-debug)

case Seq在 typer中是这样翻译的:

def f[A](seq: Seq[A])(implicit evidence$1: Ordering[A]): Boolean = seq match {
  case collection.this.Seq.unapplySeq[A](<unapply-selector>) <unapply> ((a @ _), (b @ _)) => scala.`package`.Ordering.Implicits.infixOrderingOps[A](b)(evidence$1).>(a)

在 patmat:

def f[A](seq: Seq[A])(implicit evidence$1: Ordering[A]): Boolean = {
  case <synthetic> val x1: Seq[A] = seq;
  case5(){
    <synthetic> val o7: Option[Seq[A]] = collection.this.Seq.unapplySeq[A](x1);
    if (o7.isEmpty.unary_!)
      if (o7.get.!=(null).&&(o7.get.lengthCompare(2).==(0)))
        {
          val a: A = o7.get.apply(0);
          val b: A = o7.get.apply(1);
          matchEnd4(scala.`package`.Ordering.Implicits.infixOrderingOps[A](b)(evidence$1).>(a))
        }
      else
        case6()
    else
      case6()
  };

换句话说,unapply只是返回你的 Seq,它会得到前两个元素。

应该看起来完全一样,case List除了它没有类型检查。

好的,我被其他事情分心了,比如我女儿今天开始在水下游泳,所以为了短路,这行得通:

scala> import Ordering.Implicits.infixOrderingOps
import Ordering.Implicits.infixOrderingOps

scala> import reflect.ClassTag
import reflect.ClassTag

scala> def f[X](seq: Seq[X])(implicit e1: Ordering[X], e2: ClassTag[X]) = seq match { case xs: List[X] if xs.length == 2 => xs(1) > xs(0) }
f: [X](seq: Seq[X])(implicit e1: Ordering[X], implicit e2: scala.reflect.ClassTag[X])Boolean

也许这种赤字在起作用

于 2013-08-06T04:33:45.903 回答
0

在第一种情况下,

{ case List(a, b) => b > a }

这不起作用,因为您刚刚定义了一个部分函数,​​但没有定义这些元素的 Ordering。从某种意义上说,它case List(a,b)代表一个包含aand的列表b。现在a可以意味着任何事情。这样做a:A, b:A是有效的,因为现在您定义它是一个包含 2 个 type 元素的 List A

因为我们已经为A. 因此,第二个作品。

根据第三个:seq.sliding(2).返回 a Iterator[List[A]]。对于List[A]迭代器中的每个,您正在调用s(0) > s(1). 这里s(0)s(1)是类型A。因为您已经定义了界限A: Ordering,所以您已经为 type 定义了 ordering A。在这种情况下s(0) s(1)。因此它有效。

于 2013-08-05T13:02:21.433 回答