Scala 从Int到Ordered的隐式转换问题
这是Scala中左派堆的一个实现Scala 从Int到Ordered的隐式转换问题,scala,Scala,这是Scala中左派堆的一个实现 package my.collections sealed abstract class Heap[E](implicit val ordering:Ordering[E]) { import ordering._ def empty: Heap[E] = Heap.empty def isEmpty: Boolean def insert(e: E): Heap[E] def merge(h: Heap[E]): Heap[E
package my.collections
sealed abstract class Heap[E](implicit val ordering:Ordering[E]) {
import ordering._
def empty: Heap[E] = Heap.empty
def isEmpty: Boolean
def insert(e: E): Heap[E]
def merge(h: Heap[E]): Heap[E] = {
def makeT(e:E,a:Heap[E],b:Heap[E]):Heap[E] = if (a.rank >= b.rank) Node(e,a,b,b.rank+1) else Node(e,b,a,a.rank+1)
(this,h) match {
case (Nil(),_) => h
case (_,Nil()) => this
case (Node(x,l1,r1,_),Node(y,l2,r2,_)) => if (x < y) makeT(x,l1,r1.merge(h)) else makeT(y,l2,this.merge(r2))
}
}
def findMin: E
def deleteMin: Heap[E]
protected def rank:Int
}
object Heap {
private val emptyEl = new Nil[Nothing]
def empty[E] = emptyEl.asInstanceOf[Heap[E]]
}
private case class Node[E](e: E, left: Heap[E], right: Heap[E], rank: Int)(implicit ordering:Ordering[E]) extends Heap[E]()(ordering) {
def deleteMin = left.merge(right)
val findMin = e
def insert(e: E):Heap[E] = Node(e,empty,empty,1).merge(this)
def isEmpty = false
}
private case class Nil[E]()(implicit ordering:Ordering[E]) extends Heap[E]()(ordering) {
def deleteMin = throw new NoSuchElementException
def findMin = throw new NoSuchElementException
def insert(e: E):Heap[E] = Node[E](e,Heap.empty,Heap.empty,1)
def isEmpty = true
protected def rank = 0
}
object PG {
def main(args: Array[String]) {
val e:Heap[Int] = Heap.empty[Int]
val e1:Heap[Int] = e insert 3
val e2:Heap[Int] = e1 insert 5
val e3:Heap[Int] = e2.deleteMin
println()
}
}
我的问题是:
因为您得到了一个类强制转换异常,所以我将研究代码中可能出现的错误强制转换。我可以找到一个演员:
def empty[E] = emptyEl.asInstanceOf[Heap[E]]
由于E
不是协变的,这是一个强制转换错误,Heap[Nothing]
不是Heap[E]
的子类
在这里,您将有相当多的工作要使E
协变,因此,除非您需要此功能,否则您可以修复强制转换:
object Heap {
def empty[E](implicit ordering:Ordering[E]) = new Nil[E]
}
顺便说一下,如果
Heap
在E
中是协变的(例如Heap[+E]
),您就不需要执行强制转换,因为scalac会接受您为Heap[E]
返回Nil[Nothing]
。因此,除非您确切地知道为什么要使用作为
的实例,而且没有办法避免,否则这几乎肯定是一个错误。由于您遇到了类强制转换异常,我将研究代码中可能出现的错误强制转换。我可以找到一个演员:
def empty[E] = emptyEl.asInstanceOf[Heap[E]]
由于E
不是协变的,这是一个强制转换错误,Heap[Nothing]
不是Heap[E]
的子类
在这里,您将有相当多的工作要使E
协变,因此,除非您需要此功能,否则您可以修复强制转换:
object Heap {
def empty[E](implicit ordering:Ordering[E]) = new Nil[E]
}
顺便说一下,如果
Heap
在E
中是协变的(例如Heap[+E]
),您就不需要执行强制转换,因为scalac会接受您为Heap[E]
返回Nil[Nothing]
。因此,除非你确切地知道为什么要用来代替
,而且没有办法避免,否则这几乎肯定是一个错误。好的,这里有更多的证据证明我的答案是正确的
class A[B](implicit ord: Ordering[B]) {
def compare(x: B, y: B) = ord.lt(x, y)
}
object A {
private val e = new A[Nothing] ()
def empty[X] = e.asInstanceOf[A[X]]
}
val test = A.empty[Int] // works
test.compare(1, 2) // ouch
您可以看到,对类型参数进行错误的强制转换是完全有效的!这是不幸的JVM类型擦除故事的一部分,因为强制转换发生在运行时,A[B]
和A[Nothing]
减少为[java.lang.Object],因此强制转换本身并不被禁止
真相(错误)将在稍后的时间揭晓…好的,这里有更多的证据证明我的答案是正确的
class A[B](implicit ord: Ordering[B]) {
def compare(x: B, y: B) = ord.lt(x, y)
}
object A {
private val e = new A[Nothing] ()
def empty[X] = e.asInstanceOf[A[X]]
}
val test = A.empty[Int] // works
test.compare(1, 2) // ouch
您可以看到,对类型参数进行错误的强制转换是完全有效的!这是不幸的JVM类型擦除故事的一部分,因为强制转换发生在运行时,A[B]
和A[Nothing]
减少为[java.lang.Object],因此强制转换本身并不被禁止
真相(错误)将在稍后的时间揭晓…这是我见过的最糟糕的例子之一,说明了当你对编译器撒谎时会出现什么问题!:-) 我将一行一行地展示发生了什么,这样人们就可以看到发生了什么(但0__;是正确的,应该得到公认的答案) 那叫
def empty[E] = emptyEl.asInstanceOf[Heap[E]]
哪个叫
private val emptyEl = new Nil[Nothing]
它采用隐式排序[无]
。我很惊讶有这样的事情,所以我查了一下。关于订购
,有一点是,如果您的收藏是订购的
,它将提供订购
。提供此功能的方法是:
implicit def ordered [A <: Ordered[A]]: Ordering[A] = new Ordering[A] {
def compare(x: A, y: A) = x.compare(y)
}
这将在Nil
上调用insert
:
def insert(e: E):Heap[E] = Node[E](e,Heap.empty,Heap.empty,1)
注意,没有排序[E]
被传递到方法insert
,因此它使用传递到Nil
,排序[Nothing]
。但仍然没有错误,所以下一行:
val e2:Heap[Int] = e1 insert 5
这将调用节点上的插入:
def insert(e: E):Heap[E] = Node(e,empty,empty,1).merge(this)
同样,没有传递任何Ordering[E]
,因此它使用创建时收到的,仍然是Ordering[Nothing]
。这将最终导致错误,在merge
的这一行:
case (Node(x,l1,r1,_),Node(y,l2,r2,_)) => if (x < y) makeT(x,l1,r1.merge(h)) else makeT(y,l2,this.merge(r2))
WhereOps.这是我见过的最糟糕的例子之一,当你对编译器撒谎时会出现什么问题!:-)
我将一行一行地展示发生了什么,这样人们就可以看到发生了什么(但0__;是正确的,应该得到公认的答案)
那叫
def empty[E] = emptyEl.asInstanceOf[Heap[E]]
哪个叫
private val emptyEl = new Nil[Nothing]
它采用隐式排序[无]
。我很惊讶有这样的事情,所以我查了一下。关于订购
,有一点是,如果您的收藏是订购的
,它将提供订购
。提供此功能的方法是:
implicit def ordered [A <: Ordered[A]]: Ordering[A] = new Ordering[A] {
def compare(x: A, y: A) = x.compare(y)
}
这将在Nil
上调用insert
:
def insert(e: E):Heap[E] = Node[E](e,Heap.empty,Heap.empty,1)
注意,没有排序[E]
被传递到方法insert
,因此它使用传递到Nil
,排序[Nothing]
。但仍然没有错误,所以下一行:
val e2:Heap[Int] = e1 insert 5
这将调用节点上的插入:
def insert(e: E):Heap[E] = Node(e,empty,empty,1).merge(this)
同样,没有传递任何Ordering[E]
,因此它使用创建时收到的,仍然是Ordering[Nothing]
。这将最终导致错误,在merge
的这一行:
case (Node(x,l1,r1,_),Node(y,l2,r2,_)) => if (x < y) makeT(x,l1,r1.merge(h)) else makeT(y,l2,this.merge(r2))
在这里,Ops。为了补充这一点,在我看来问题在于,通过调用Nil[Nothing]
,它是Ordering[Nothing]
作为隐式传递的,并且它在其他地方被接受,因为是一个
谎言的替代。当然,你是对的,这对我来说完全是一个白痴时刻,但这是可以修复的,因此空节点只有一个实例吗?@user44242为什么只需要一个实例?正如我所说,您可以尝试在E
中使Heap
协变,然后您可以使用Nil[无]
。Scala的List
类就是这样构造的,Nil
是一个case对象。使堆协变是非常不寻常的,因为排序要么是不变的,要么是逆变的(scalaz)。Nil是case对象,因为不涉及不变类型。原因很简单,不需要同时有多个空对象的副本