Ошибка PriorityQueue varargs при расширении Ordering для размещения объектов класса

Моя цель - создать функцию, которая принимает varargs из 2 или более объектов при инициализации PriorityQueue, содержащего указанные объекты.

Соответствующий код:

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):

[error] found : TopicTrenderInit.FreqOrdering.type
[error] required: scala.math.Ordering[Equals]
[error] Note: TopicTrenderInit.Topic <: Equals (and TopicTrenderInit.FreqOrdering.type <: scala.math.Ordering[TopicTrenderInit.Topic]), but trait Ordering is invariant in type T.
[error] You may wish to investigate a wildcard type such as _ <: Equals. (SLS 3.2.10)
[error] return PriorityQueue(a,b,c)(FreqOrdering)
[error] ^
[error] /home/aaron-laptop/Documents/Scala/topic_trender100/src/main/scala/main.scala:48:25: type mismatch;
[error] found : scala.collection.mutable.PriorityQueue[Equals]
[error] required: scala.collection.mutable.PriorityQueue[TopicTrenderInit.Topic]
[error] Note: Equals >: TopicTrenderInit.Topic, but class PriorityQueue is invariant in type A.
[error] You may wish to investigate a wildcard type such as _ >: TopicTrenderInit.Topic. (SLS 3.2.10)
[error] return PriorityQueue(a,b,c)(FreqOrdering)

Когда нет "*", указывающего на vararg, все работает, ошибок нет. Я думаю, что меня больше всего смущает то, что я вижу ошибку required: scala.math.Ordering [Equals]. Я также прочитал статью о сопоставлении с образцом, но мне кажется, что мне нужно будет прочитать об этом больше, чтобы понять реализацию. Что здесь происходит? Спасибо.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема в том, что когда вы передаете a, b, c в ФабрикаPriorityQueue. Компиляторы видят, что вы передали три аргумента типа A, и единственный супертип между этими деревьями - Equals.
. Это потому, что a и b - это Topics, который как класс случая расширяет Equals, а c относится к типу Seq[Topic](аргументы varargs передаются как Seq), который также расширяет Equals.
. Вот почему он просит Ordering[Equals].

Вы можете исправить это следующим образом. (Обратите внимание, это довольно уродливо и, возможно, неуместно, вы можете просто получить один varargs вместо a и b, а затем c)

// 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): _*)
Ответ принят как подходящий

Проблема в том, как вы собираете 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, вероятно, будет списком во время выполнения) будет лучше объединить оба Seqs или использовать flatten?

Luis Miguel Mejía Suárez 31.12.2018 18:18

@ LuisMiguelMejíaSuárez, я думаю, что для List самой быстрой реализацией будет a +: b +: c:_*, потому что он будет использовать +: из List, который будет просто оболочкой для ::, то есть O(1). Однако для других базовых коллекций производительность может быть очень плохой, потому что может потребоваться 2 копии. Предлагаемый здесь ++ для List - это оболочка над :::, которая также будет неплохой без больших жертв для других возможных типов. AFAIK ваш flatten не переопределяется ни в одном классе, поэтому оптимизации нет вообще, и на самом деле сложнее добавить оптимизацию в flatten.

SergGr 31.12.2018 20:05

@SergGr после небольшого поиска я обнаружил, что varargs реализованы с использованием WrappedArray, а не List, поэтому да, решение Тима с использованием ++ будет лучшим. Я обновлю свой ответ, чтобы использовать его тоже.

Luis Miguel Mejía Suárez 31.12.2018 20:20

спасибо, все эти комментарии поучительны. В чем причина не использовать ключевое слово return? Это для лучшей читаемости или по более функциональной причине?

A.Lopez 02.01.2019 19:40

@ A.Lopez Проблема с return заключается в том, что он не нужен в простых случаях и может иметь неожиданное поведение в сложных случаях. В простом случае вы можете просто использовать выражение в качестве возвращаемого значения. В сложных случаях он может выпрыгивать из нескольких вложенных блоков, и не всегда понятно, куда он перескакивает!

Tim 02.01.2019 19:58

Другие вопросы по теме