通用优先级队列中的协变和逆变类型

发布于 2024-09-12 04:29:16 字数 1839 浏览 12 评论 0原文

我正在尝试在 Scala 中实现一个在类型 T 上参数化的通用数据类型,该类型应该是 Ordered[T]。具体来说,它是 Sleator & 的持久版本。 Tarjan 的倾斜堆优先级队列。根据此处的解释添加大量复杂的类型参数声明后在 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]

I'm trying to implement in Scala a generic data type parameterized on a type T, which should be Ordered[T]. Specifically, it's a persistent version of Sleator & Tarjan's skew heap priority queues. After adding lots of complicated type parameter declarations based on the explanation here and in Odersky-Spoon-Venners, I'm down to one compiler error before I can test/debug the actual functionality.

Below is a simplified version of my code.

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
}

This gives the following error:

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

I've tried several variants of the declaration of delMin, but to no avail. I think I understand the problem (method + wants an ordering guarantee), but where should I put this? And is there a way to declare delMin as returning SkewHeap[T] instead of SkewHeap[U]?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

遥远的绿洲 2024-09-19 04:29:16
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.minOrdered[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
}

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

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 isn't sure how to compare this.min with that.min, becuase it wants to convert this.min to an Ordered[T] and that.min to an Ordered[U]. The simplest answer is to add a type conversion to force this.min to an 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
}

But you have a big problem with all of these implicits, and that problem is that you could get a different Ordered implementation in every context where you use the view bound <% Ordered[Something], so you should really look for some other way of making sure your ordering is consistent.

小梨窩很甜 2024-09-19 04:29:16

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

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

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

另外,关于习语的一个小注意事项:习惯上使用 ++ 运算符来连接两个集合,并使用 + 运算符将单个值添加到现有集合(请参阅 VectorArrayBuffer 以及标准库中的几乎所有集合)。

Rather than using the <% syntactic sugar, I suggest that you manually add the implicit parameter. It's a lot more controlled, and certainly easier to see what's going on:

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

The problem with using the <% operator in your case is it binds to T rather than U. Thus, it was looking for a function of type T => Ordered[U]. In fact, all of your methods are doing this, and I suspect that's not the behavior you wanted.

Also, a minor note on idioms: it is customary to use the ++ operator for concatenating two collections, and the + operator for adding a single value to an existing collection (see Vector, ArrayBuffer, and pretty much any collection in the standard library).

甜柠檬 2024-09-19 04:29:16

除了其他建议之外,您还可以考虑从 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...)

Additional to the other suggestions, you may consider to switch from Ordered to an implicit parameter Ordering[T], which is much easier to control and gives you more flexibility.

[Edit]
A very simple example:

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
}

After this you can use Foo for all types that have an ordering. Of course you can make one yourself:

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

After this, you could create a Foo[Bar].

(Sorry for the very basic example, my PC broke down and I have no IDE available...)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文