Seq [A]扩展了Ordered [Seq [A]]

时间:2011-03-11 15:42:40

标签: scala

我正在努力制作一个与其他序列相当的序列(例如,其他集合类型也是可以想象的)。

class RichSeq[A](val seq: Seq[A]) extends Ordered[RichSeq[A]]

当然在引用的包对象中存在隐式转换:

implicit def seq2RichSeq[A](s: Seq[A]) = new RichSeq(s)

比较意味着,第一个尺寸比每个元素都重要。代码明确指出:

class RichSeq[A](val seq: Seq[A]) extends Ordered[RichSeq[A]] { 
  def compare(s: RichSeq[A]) = {
    seq.size compare s.seq.size match {
      case 0 => seq.view.zip(s.seq).map { case (x,y) => ord.compare(x,y) }.dropWhile(_ == 0).headOption.getOrElse(0)
      case x => x
    }
  }
}

但那不会编译(当然),因为需要一个排序来比较元素,所以我试过了:

class RichSeq[A](val seq: Seq[A]) extends Ordered[RichSeq[A]] { 
  def compare(s: RichSeq[A])(implicit ord: Ordering[A]) = {
    // ...
  }
}

现在比较方法的签名不合适,所以我将隐式ord移到了类签名(并调整了隐式转换):

implicit def seq2RichSeq[A](s: Seq[A])(implicit ord: Ordering[A]) = new RichSeq(s)
class RichSeq[A](val seq: Seq[A])(implicit ord: Ordering[A]) extends Ordered[RichSeq[A]] { 
  def compare(s: RichSeq[A]) = {
      // ...
    }
  }

但是现在我遇到了一个问题,RichSeq中我希望通过implicit Seq[A]使用的所有其他方法也需要隐式Ordering[A]而我无法一直提供一个。有时我使用我的RichSeq方法而不使用Ordering,有时使用比较方法。

例如,有时我打电话

def distinctBy[B](f: A => B): Seq[A] = {
  seq.foldLeft { (Buffer[A](),MutMap[B,A]()) } {
    case ((b,m),x) if m contains f(x) => (b,m)
    case ((b,m),x) => 
      m += f(x) -> x
      b += x
      (b,m)
  }._1
}

同时我无法定义Ordering[A]

我看到一个解决方案有两个不同的类(有两个隐式转换):

class RichSeqOrderable[A](val seq: Seq[A])(implicit ord: Ordering[A]) extends Ordered[RichSeqOrderable[A]]

class RichSeq[A](val seq: Seq[A])

但我认为这打破了将所有东西放在一起的想法?!

3 个答案:

答案 0 :(得分:3)

我通常的序言是,我不一定会以这种方式做事,而是以问题为借口来阐明一些鲜为人知的特征:如果有任何隐式排序可用,它将使用它,但否则它会命令它们通过哈希码。

package object foo {
  implicit def seq2RichSeq[A](s: Seq[A])(implicit ord: Ordering[A] = Ordering[Int].on((_: A).##)) = new RichSeq(s)
}
package foo {
  class RichSeq[A](val seq: Seq[A])(implicit ord: Ordering[A]) extends Ordered[RichSeq[A]] { 
    def compare(s: RichSeq[A]) = {
      seq.size compare s.seq.size match {
        case 0 => seq.view.zip(s.seq).map { case (x,y) => ord.compare(x,y) }.dropWhile(_ == 0).headOption.getOrElse(0)
        case x => x
      }
    }
  }
}

答案 1 :(得分:1)

我选择了类似于paulp的建议:

class RichSeq[A](val seq: Seq[A])(implicit optionalOrd: Option[Ordering[A]] = None) extends Ordered[RichSeq[A]] {
  def compare(s: RichSeq[A]) = {
    seq.size compare s.seq.size match {
      case 0 => seq.view.zip(s.seq).map { case (x,y) => optionalOrd.map(_.compare(x,y)).getOrElse(0) }.dropWhile(_ == 0).headOption.getOrElse(0)
      case x => x
    }
  }
}

object RichSeq {
  implicit def orderingToSome[A](implicit ord: Ordering[A] = null) = Option(ord)
  implicit def seq2RichSeq[A](s: Seq[A])(implicit ord: Option[Ordering[A]]) = new RichSeq(s)
}

拥有太多的含义,特别是标准库中的类型,并不是一件好事。但是,我认为Ordering[A] => Option[Ordering[A]]尽可能安全。

关于隐含链接

Scala对自动转换的限制是添加方法,即在尝试查找方法时不会应用多个转换。例如:

class A(val n: Int)
class B(val m: Int, val n: Int)
class C(val m: Int, val n: Int, val o: Int) {
  def total = m + n + o
}

// This demonstrates implicit conversion chaining restrictions
object T1 { // to make it easy to test on REPL
  implicit def toA(n: Int) = new A(n)
  implicit def aToB(a: A) = new B(a.n, a.n)
  implicit def bToC(b: B) = new C(b.m, b.n, b.m + b.n)

  // won't work
  println(5.total)
  println(new A(5).total)

  // works
  println(new B(5, 5).total)
  println(new C(5, 5, 10).total)
}

但是,如果隐式定义本身需要隐式参数,则Scala 根据需要查找其他隐式值。从最后一个例子继续:

// def m[A <% B](m: A) is the same thing as
// def m[A](m: A)(implicit ev: A => B)

object T2 {
  implicit def toA(n: Int) = new A(n)
  implicit def aToB[A1 <% A](a: A1) = new B(a.n, a.n)
  implicit def bToC[B1 <% B](b: B1) = new C(b.m, b.n, b.m + b.n)

  // works
  println(5.total)
  println(new A(5).total)
  println(new B(5, 5).total)
  println(new C(5, 5, 10).total)
}
你可能会说,“魔术!”不是这样。以下是编译器如何翻译每一个:

object T1Translated {
  implicit def toA(n: Int) = new A(n)
  implicit def aToB(a: A) = new B(a.n, a.n)
  implicit def bToC(b: B) = new C(b.m, b.n, b.m + b.n)

  // Scala won't do this
  println(bToC(aToB(toA(5))).total)
  println(bToC(aToB(new A(5))).total)

  // Just this
  println(bToC(new B(5, 5)).total)

  // No implicits required
  println(new C(5, 5, 10).total)
}

object T2Translated {
  implicit def toA(n: Int) = new A(n)
  implicit def aToB[A1 <% A](a: A1) = new B(a.n, a.n)
  implicit def bToC[B1 <% B](b: B1) = new C(b.m, b.n, b.m + b.n)

  // Scala does this
  println(bToC(5)(x => aToB(x)(y => toA(y))).total)
  println(bToC(new A(5))(x => aTo(B(x)(identity _)).total)
  println(bToC(new B(5, 5))(identity _).total)

  // no implicits required
  println(new C(5, 5, 10).total)
}

因此,当bToC被用作隐式转换时,aToBtoA将作为隐式参数传递,而不是被链接为隐式转化

答案 2 :(得分:0)

我没有完全编码,但你为什么不这样做:

class RichSeq[A <: Ordered[A]](val seq: Seq[A]) extends Ordered[RichSeq[A]] {
  import Ordering.ordered 

  ...
}

导入的隐式转化将在必要时为您提供Ordering[A]