扩展 Ordering 以容纳类对象时出现 PriorityQueue 可变参数错误

Posted

技术标签:

【中文标题】扩展 Ordering 以容纳类对象时出现 PriorityQueue 可变参数错误【英文标题】:PriorityQueue varargs error when extending Ordering to accomodate class objects 【发布时间】:2018-12-31 16:55:54 【问题描述】:

我的目标是创建一个函数,该函数在初始化包含所述对象的 PriorityQueue 时采用 2 个或更多对象的可变参数。

相关代码为:

case class Topic(topic: String, usageFrequency: Long = 1)  

object FreqOrdering extends Ordering[Topic] 
  def compare(a: Topic, b:Topic) = -(a.usageFrequency compare b.usageFrequency)  

def initPriQu(a : Topic, b: Topic, c: Topic*): PriorityQueue[Topic] = 
return PriorityQueue(a,b,c)(FreqOrdering)

sbt (Scala 2) 中的错误:

[错误] 发现:TopicTrenderInit.FreqOrdering.type [错误] 必需:scala.math.Ordering[Equals] [错误] 注意:TopicTrenderInit.Topic <: equals topictrenderinit.freqordering.type scala.math.ordering trait ordering t> [错误] 您可能希望调查诸如_ &lt;: Equals 之类的通配符类型。 (SLS 3.2.10) [错误] 返回 PriorityQueue(a,b,c)(FreqOrdering) [错误] ^ [错误] /home/aaron-laptop/Documents/Scala/topic_trender100/src/main/scala/main.scala:48:25:类型不匹配; [错误] 发现:scala.collection.mutable.PriorityQueue[Equals] [错误] 必需:scala.collection.mutable.PriorityQueue[TopicTrenderInit.Topic] [错误] 注意:Equals >: TopicTrenderInit.Topic,但类 PriorityQueue 在类型 A 中是不变的。 [错误] 您可能希望调查诸如_ &gt;: TopicTrenderInit.Topic 之类的通配符类型。 (SLS 3.2.10) [错误] return PriorityQueue(a,b,c)(FreqOrdering)

当没有 '*' 表示可变参数时,一切正常,没有错误。我认为最让我困惑的是 required: scala.math.Ordering[Equals] 我看到的错误。我还阅读了一篇关于模式匹配的文章,但我觉得我必须阅读更多内容才能理解实现。这是怎么回事? 谢谢。

【问题讨论】:

【参考方案1】:

问题在于您构建PriorityQueue 的方式。您正在向它传递两个 Topic 类型的值和一个 Seq[Topic] 类型的值,因此结果是 PriorityQueue[Any]

这应该可行:

def initPriQu(a : Topic, b: Topic, c: Topic*): mutable.PriorityQueue[Topic] =
  mutable.PriorityQueue(Seq(a, b) ++ c:_*)(FreqOrdering)

另外,不要使用return

【讨论】:

在性能方面(并且知道 Seq 在运行时可能是一个 List) 连接两个 Seq 或使用 flatten 会更好吗? @LuisMiguelMejíaSuárez,我认为对于List,最快的实现将是a +: b +: c:_*,因为它将使用来自List+:,这将只是:: 的包装,即@987654332 @。但是,对于其他基础集合,性能可能非常糟糕,因为可能需要 2 个副本。这里建议的++List::: 的一个包装器,它也非常好,而不会为其他可能的类型做出任何重大牺牲。 AFAIK 你的flatten 没有在任何类中被覆盖,所以根本没有优化,实际上更难为flatten 添加优化。 @SergGr 经过一番搜索后,我发现 varargs 是使用WrappedArray 而不是List 实现的,所以是的,蒂姆使用++ 的解决方案将是最好的一。我也会更新我的答案以使用它。 谢谢,所有这些 cmets 都很有启发性。不使用 return 关键字的原因是什么?是为了更好的可读性还是更实用的原因? @A.Lopez return 的问题是在简单情况下没有必要,在复杂情况下可能会出现意外行为。在简单的情况下,您可以只使用表达式作为返回值。在复杂的情况下,它可以跳出多个嵌套块,并且并不总是清楚它跳到哪里!【参考方案2】:

问题在于,当您将a, b, c 传递给PriorityQueueFactory 时。编译器看到的是您传递了三个A 类型的参数,而这些树之间唯一的超类型是Equals。 这是因为abTopics,作为案例类扩展Equals,而c 的类型为Seq[Topic] (可变参数作为Seq 传递) ,它也扩展了Equals。 这就是为什么它要求Ordering[Equals]

您可以按如下方式修复它。(请注意,这很丑陋,可能很无用,您可以考虑只接收一个可变参数而不是a & b 然后c) em>

// It will be good to have this as an implicit so you don't have to pass it explicitly
// every time you need to.
// And it is always preferable to have an implicit val with an explicit type signature
// than an implicit object.
implicit val TopicOrdering: Ordering[Topic] = new math.Ordering[Topic] 
  override def compare(a: Topic, b:Topic): Int =
    -(a.usageFrequency compare b.usageFrequency)


import scala.collection.mutable.PriorityQueue
def initPriQu(a: Topic, b: Topic, others: Topic*): PriorityQueue[Topic] =
  // 1. Don't use return in scala.
  // 2. Here I made a Seq of Seqs of Topic - Seq[Seq[Topic]]
  //    then I flatten it to have a Seq of Topic - Seq[Topic]
  //    and finally used the ':_*' operator to turn a Seq into a varargs.
  PriorityQueue((Seq(a, b) ++ others): _*)

【讨论】:

以上是关于扩展 Ordering 以容纳类对象时出现 PriorityQueue 可变参数错误的主要内容,如果未能解决你的问题,请参考以下文章

扩展类异常时出现PHP“找不到类”错误

升级到 Apollo 客户端 2 后尝试向结果 gql 数据对象添加字段时出现“对象不可扩展”

访问方法时出现 IllegalAccess 错误

打开带有 2 个“扩展名”的文件时出现 Jet 错误 3011

扩展 UIViewControllerAnimatedTransitioning 时出现分段错误

在 C++ 中将类实例添加到对象层次结构时出现问题