Scala实现了一个通用的Heap

时间:2014-07-30 17:42:39

标签: scala generics covariance contravariance variance

我正在尝试实现一个通用堆的非常基本的实现,虽然我喜欢类型检查器,但这是其中一种情况,我觉得它在每一步都在与我作斗争。

我能写的最简单的表达方式是:

trait Heap[A] {
  def isEmpty: Boolean
  def merge(as: Heap[A]): Heap[A]
  def insert(a: A): Heap[A]
  def findMin: A
  def deleteMin(): Heap[A]
}

这很好,但只要调用mergeinsertdeleteMin,具体实现就会“松散”其类型。也就是说,如果set的类型为CustomHeap,则调用set.deleteMin的类型为Heap

经过一番努力,我想出了解决这个问题的以下定义:

trait Heap[A, Repr <: Heap[A, Repr]] {
  def isEmpty: Boolean
  def merge(as: Repr): Repr
  def insert(a: A): Repr
  def findMin: A
  def deleteMin(): Repr
}

这开始变得复杂,但按预期工作:它是通用的Heap,例如,在调用merge时类型不会丢失。

当一个人试图不将一个人的代码与Heap的特定实现联系起来时,这个定义有点麻烦,但是:变量不能是Heap[A]类型,而是一些更复杂的东西我很快不要试图写作。

为了解决这个限制,我尝试使用集合API中到处找到的XxxLike模式,但这就是我遇到的问题。

到目前为止,这就是我所拥有的:

trait Heap[A] extends HeapLike[A, Heap[A]]

trait HeapLike[A, +Repr <: HeapLike[A, Repr] with Heap[A]] {
  def isEmpty: Boolean
  def merge(bs: Repr): Repr
  def insert(a: A): Repr
  def findMin: A
  def deleteMin(): Repr
}

这有点复杂,并且只是为了打字而引入了一个新的特性,但我可以忍受 - 如果它有效。

通过此实施,HeapLikeRepr的协变,但Reprmerge的参数 - 逆变位置的协变类型。我无法解决这个问题。

我还尝试在HeapLike上制作Repr非变体,这种方式正常,直到我尝试实际混合特征:

sealed trait LeftistHeap[A] extends Heap[A] with HeapLike[A, LeftistHeap[A]] {
  def rank: Int
}

这会产生以下错误消息:

error: illegal inheritance;
self-type this.LeftistHeap[A] does not conform to this.HeapLike[A,this.LeftistHeap[A]]'s selftype this.HeapLike[A,this.LeftistHeap[A]]
sealed trait LeftistHeap[A] extends Heap[A] with HeapLike[A, LeftistHeap[A]] {

我确信有一种简单的方法可以让这整件事情发挥作用 - 它比集合API更基本,它设法完成所有这些对包含的元素的协变一个集合,但我觉得我已经碰到了一堵砖墙。任何建议,解释,指示......?

1 个答案:

答案 0 :(得分:0)

实际上,Scala中的差异非常棒,可以让您更简单地解决问题。

让我们采用你原来的Heap特质:

trait Heap[A] {
  def isEmpty: Boolean
  def merge(as: Heap[A]): Heap[A]
  def insert(a: A): Heap[A]
  def findMin: A
  def deleteMin(): Heap[A]
}

...为了使派生集合在应用转换后保持其“种类”,您可以执行以下操作(我在此处演示概念,因此我使用???来存储方法。此代码将编译但是您显然必须实现它们以使您的堆在运行时工作):

class MyHeap[A] extends Heap[A] {
  override def isEmpty: Boolean = ???
  override def insert(a: A): MyHeap[A] = ???
  override def deleteMin(): MyHeap[A] = ???
  override def merge(as: Heap[A]): MyHeap[A] = ???
  override def findMin: A = ???
}

...然后你可以这样做:

val myHeap1 = new MyHeap[Int]
val myHeap2 = new MyHeap[Int]

val stillMyHeap1: MyHeap[Int] = myHeap1 insert 1
val stillMyHeap2: MyHeap[Int] = myHeap1 merge myHeap2

请注意insertdeleteMinmerge return types are covariantMyHeap)。

另请注意,merge采用相同类型的参数(Heap),但如果您满意,也可以将其实施为take contravariant argument type(即Heap的假设基础)。

<强>更新

BTW,使Heap[A](或MyHeap[A])保留其参数类型A的方差,就像标准的Scala集合一样(例如:AnyVal作为基础在IntHeap[AnyVal]成为Heap[Int]的基础),你必须改变你的Heap特征:

trait Heap[+A] {
  def isEmpty: Boolean
  def merge[B >: A](xs: Heap[B]): Heap[B]
  def insert[B >: A](x: B): Heap[B]
  def findMin: A
  def deleteMin(): Heap[A]
}

......和MyHeap这样:

class MyHeap[+A] extends Heap[A] {
  override def isEmpty: Boolean = ???
  override def insert[B >: A](x: B): MyHeap[B] = ???
  override def deleteMin(): MyHeap[A] = ???
  override def merge[B >: A](xs: Heap[B]): MyHeap[B] = ???
  override def findMin: A = ???
}

...然后你可以按照以下方式玩它:

val myHeap1 = new MyHeap[Int]
val myHeap2 = new MyHeap[Int]

val stillMyHeap1: MyHeap[Int] = myHeap1 insert 1
val stillMyHeap2: MyHeap[Int] = myHeap1 merge myHeap2

val myHeap3: MyHeap[AnyVal] = myHeap1
val myHeap4: MyHeap[AnyVal] = myHeap1 insert 'a'

val heap1: Heap[Int] = myHeap1
val heap2: Heap[AnyVal] = myHeap1

请注意MyHeap[Int]如何符合所有这些:

  • MyHeap[AnyVal]myHeap3
  • Heap[Int]heap1
  • Heap[AnyVal]heap2

还请注意insert Char MyHeap[Int] MyHeap[AnyVal]如何产生myHeap4MyHeap[Int])。由于此操作的结果不能同时为MyHeap[Char]MyHeap[AnyVal],因此它会回溯到两个共同的祖先,即{{1}}