我正在尝试实现一个通用堆的非常基本的实现,虽然我喜欢类型检查器,但这是其中一种情况,我觉得它在每一步都在与我作斗争。
我能写的最简单的表达方式是:
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]
}
这很好,但只要调用merge
,insert
或deleteMin
,具体实现就会“松散”其类型。也就是说,如果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
}
这有点复杂,并且只是为了打字而引入了一个新的特性,但我可以忍受 - 如果它有效。
通过此实施,HeapLike
是Repr
的协变,但Repr
是merge
的参数 - 逆变位置的协变类型。我无法解决这个问题。
我还尝试在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更基本,它设法完成所有这些和对包含的元素的协变一个集合,但我觉得我已经碰到了一堵砖墙。任何建议,解释,指示......?
答案 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
请注意insert
,deleteMin
和merge
return types are covariant(MyHeap
)。
另请注意,merge
采用相同类型的参数(Heap
),但如果您满意,也可以将其实施为take contravariant argument type(即Heap
的假设基础)。
<强>更新强>
BTW,使Heap[A]
(或MyHeap[A]
)保留其参数类型A
的方差,就像标准的Scala集合一样(例如:AnyVal
作为基础在Int
,Heap[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]
如何产生myHeap4
(MyHeap[Int]
)。由于此操作的结果不能同时为MyHeap[Char]
和MyHeap[AnyVal]
,因此它会回溯到两个共同的祖先,即{{1}}