【问题标题】:Co- and contravariant types in generic priority queue通用优先级队列中的协变和逆变类型
【发布时间】:2010-07-30 17:10:01
【问题描述】:

我正在尝试在 Scala 中实现一个在类型 T 上参数化的通用数据类型,它应该是 Ordered[T]。具体来说,它是 Sleator & Tarjan 的 skew heap 优先级队列的持久版本。在根据 here 和 Odersky-Spoon-Venners 的解释添加了大量复杂的类型参数声明后,在测试/调试实际功能之前,我遇到了一个编译器错误。

以下是我的代码的简化版本。

abstract class SkewHeap[+T] {
  // merge two heaps
  def +[U >: T <% Ordered[U]](x : SkewHeap[U]) : SkewHeap[U]
  // remove least element, return new heap
  def delMin[U >: T <% Ordered[U]] : SkewHeap[U]
  def isEmpty : Boolean
  def min : T
  def left  : SkewHeap[T]
  def right : SkewHeap[T]
}

case object Leaf extends SkewHeap[Nothing] {
  def +[U <% Ordered[U]](that : SkewHeap[U]) = that
  def isEmpty = true
}

case class Node[+T](left : SkewHeap[T],
                    min : T,
                    right : SkewHeap[T]) extends SkewHeap[T] {
  def +[U >: T <% Ordered[U]](that : SkewHeap[U]) : SkewHeap[U] =
    that match {
      case Leaf        => this
      case Node(l,y,r) => if (this.min < that.min)
                            Node(this.right + that, this.min, this.left)
                          else
                            Node(this + that.right, that.min, that.left)
    }

  def delMin[U >: T <% Ordered[U]] : SkewHeap[U] = left + right
  def isEmpty = false
}

这会产生以下错误:

skew.scala:28: error: no implicit argument matching parameter type (T) => Ordered[T] was found.
   def delMin[U >: T <% Ordered[U]] : SkewHeap[U] = left + right

我尝试了delMin 声明的几种变体,但无济于事。我想我理解了这个问题(方法+ 想要订购保证),但是我应该把它放在哪里?有没有办法将delMin 声明为返回SkewHeap[T] 而不是SkewHeap[U]

【问题讨论】:

    标签: scala covariance priority-queue abstract-data-type


    【解决方案1】:
    abstract class SkewHeap[+T <% Ordered[T]] {
      // merge two heaps
      def +[U >: T <% Ordered[U]](x : SkewHeap[U]) : SkewHeap[U]
      // remove least element, return new heap
      def delMin : SkewHeap[T]
      def isEmpty : Boolean
      def min : T
      def left  : SkewHeap[T]
      def right : SkewHeap[T]
    }
    
    case object Leaf extends SkewHeap[Nothing] {
      def +[U <% Ordered[U]](that : SkewHeap[U]) = that
      def isEmpty = true
      def min = throw new RuntimeException
      def left = throw new RuntimeException
      def right = throw new RuntimeException
      def delMin = throw new RuntimeException
    }
    

    Scala 不确定如何比较this.minthat.min,因为它想将this.min 转换为Ordered[T]that.min 转换为Ordered[U]。最简单的答案是添加类型转换以强制将this.min 转换为Ordered[U]

    case class Node[+T <% Ordered[T]](left : SkewHeap[T],
                        min : T,
                        right : SkewHeap[T]) extends SkewHeap[T] {
      def +[U >: T <% Ordered[U]](that : SkewHeap[U]) : SkewHeap[U] =
        that match {
          case Leaf        => this
          case Node(l,y,r) => if ((this.min:Ordered[U]) < that.min)
                                Node(this.right + that, this.min, this.left)
                              else
                                Node(this + that.right, that.min, that.left)
        }
    
      def delMin : SkewHeap[T] = left + right
      def isEmpty = false
    }
    

    但是你对所有这些隐式都有一个很大的问题,这个问题是你可以在每个使用视图绑定 &lt;% Ordered[Something] 的上下文中得到不同的 Ordered 实现,所以你真的应该寻找其他的确保您的订单一致的方法。

    【讨论】:

      【解决方案2】:

      建议您手动添加隐式参数,而不是使用&lt;% 语法糖。它更受控制,当然更容易看到发生了什么:

      def delMin[U >: T](implicit ord: U => Ordered[U]): SkewHeap[U] = left + right
      

      在您的情况下使用&lt;% 运算符的问题在于它绑定到T 而不是U。因此,它正在寻找T =&gt; Ordered[U] 类型的函数。事实上,你所有的方法都是这样做的,我怀疑这不是你想要的行为。

      另外,关于习语的小提示:习惯上使用++ 运算符连接两个集合,使用+ 运算符向现有集合添加单个值(请参阅VectorArrayBuffer ,以及标准库中的几乎所有集合)。

      【讨论】:

      • 不,它绑定到Uscala&gt; def +[U &gt;: T &lt;% Ordered[U]] =0; $plus: [U &gt;: T](implicit evidence$1: (U) =&gt; Ordered[U])Int.
      【解决方案3】:

      除了其他建议之外,您可以考虑从 Ordered 切换到隐式参数 Ordering[T],这更容易控制并为您提供更大的灵活性。

      [编辑] 一个很简单的例子:

      class Foo[T](val t:T)(implicit val ord: Ordering[T]) {
         def min(that:Foo[T]) = if (ord.compare(this.t, that.t) < 0) this else that
      }
      

      在此之后,您可以将 Foo 用于所有具有排序的类型。当然你也可以自己做一个:

      implicit object barOrdering extends Ordering[Bar] {...}
      

      在此之后,您可以创建一个Foo[Bar]

      (对不起,这个非常基本的例子,我的电脑坏了,我没有可用的 IDE...)

      【讨论】:

      • 好的,我该怎么做? +1,如果您可以通过示例向我指出一个很好的解释。我发现 Programming in ScalaProgramming Scala 对这些问题都不是很清楚(但还没有从头到尾阅读)。
      • 我仍然无法实施您的建议。它抱怨“没有隐式参数匹配参数类型Ordering[Nothing]”和其他类型错误。
      猜你喜欢
      • 2013-11-29
      • 1970-01-01
      • 1970-01-01
      • 2020-01-25
      • 2010-09-29
      • 1970-01-01
      • 2011-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多