Scala 从Int到Ordered的隐式转换问题

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

这是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] = {
    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))
    

    Where
    Ops.这是我见过的最糟糕的例子之一,当你对编译器撒谎时会出现什么问题!:-)

    我将一行一行地展示发生了什么,这样人们就可以看到发生了什么(但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对象,因为不涉及不变类型。原因很简单,不需要同时有多个空对象的副本