Упорядочение, упорядочение и сравнение параметров

#scala

#scala

Вопрос:

Учитывая:

 case class Person(name: String)
  

и пытаюсь сделать:

 scala> List(Person("Tom"), Person("Bob")).sorted
  

в результате появляется жалоба на отсутствие упорядочения.

 <console>:8: error: could not find implicit value for parameter ord: Ordering[Person]
   List(Person("Tom"), Person("Bob")).sorted
  

Однако это:

 case class Person(name: String) extends Ordered[Person] {
  def compare(that: Person) = this.name compare that.name }
  

работает нормально, как и ожидалось:

 scala> List(Person("Tom"), Person("Bob")).sorted
res12: List[Person] = List(Person(Bob), Person(Tom))
  

хотя здесь нет никакого упорядочения или последствий.

Вопрос № 1: что здесь происходит? (Мои деньги на что-то неявное …)

Однако, учитывая вышеизложенное и тот факт, что это:

 scala> Person("Tom") > Person("Bob")
res15: Boolean = true
  

работает, и это также:

 scala> List(Some(2), None, Some(1)).sorted
  

работает из коробки:

 res13: List[Option[Int]] = List(None, Some(1), Some(2))
  

Я ожидал бы, что это:

 scala> Some(2) > Some(1)
  

также будет работать, однако это не так:

 <console>:6: error: value > is not a member of Some[Int]
       Some(2) > Some(1)
  

Вопрос № 2: почему нет, и как я могу заставить его работать?

Ответ №1:

Если вы установите бонусные импликации «слишком волшебный для области действия по умолчанию», вы можете сравнить параметры следующим образом:

 scala> import scala.math.Ordering.Implicits._
import scala.math.Ordering.Implicits._

scala> def cmpSome[T: Ordering](x: Option[T], y: Option[T]) = x < y
cmpSome: [T](x: Option[T], y: Option[T])(implicit evidence$1: Ordering[T])Boolean
  

Импорт дает вам неявный переход от упорядочения к классу с помощью операций с инфиксами, так что достаточно иметь упорядочение без другого импорта.

Комментарии:

1. Лучше… но все еще много работы, по моему, возможно, не столь скромному мнению. 🙂 Я понимаю, что могут быть причины, по которым это так, но с точки зрения пользователя было бы логично, что вы могли бы сравнить два варианта без каких-либо искажений, учитывая, что вы можете отсортировать их список без каких-либо искажений … поскольку для сортировки вам нужно сравнивать в другом, верно?

2. Вы можете сортировать их, потому что вы вызываете метод, который принимает неявный порядок. Здесь вы пишете метод, который принимает неявный порядок. Где-то порядок должен войти в картину, потому что произвольные параметры [T] несопоставимы.

Ответ №2:

Что касается вашего первого вопроса: Ordered[T] расширяется Comparable[T] . Ordering Сопутствующий объект предоставляет неявное Ordering[T] значение для любого значения, которое может быть преобразовано в Comparable[T] :

 implicit def ordered[A <% Comparable[A]]: Ordering[A]
  

Нет неявного преобразования A : Ordering => Ordered[A] — вот почему Some(1) > Some(2) не будет работать.

Сомнительно, является ли хорошей идеей определять такое преобразование, поскольку вы можете в конечном итоге обернуть свои объекты в Ordered экземпляры, а затем снова создать Ordering их (и так далее …). Хуже того: вы могли бы создать два Ordered экземпляра с разными Ordering экземплярами в области видимости, что, конечно, не то, что вы хотите.

Ответ №3:

Определение метода List sorted является:

 def sorted [B >: A] (implicit ord: Ordering[B]): List[A]
  

Итак, да, неявные вещи происходят, но многие классы в стандартной библиотеке имеют связанные с ними неявные объекты без необходимости их предварительного импорта.

Сопутствующий объект упорядочения определяет набор неявных упорядочений. Среди них есть OptionOrdering и IntOrdering, которые помогают объяснить способность списка вызывать sorted .

Чтобы получить возможность использовать операторы, когда доступно неявное преобразование, вам необходимо импортировать этот объект, например:

 def cmpSome(l:Option[Int], r:Option[Int])(implicit ord:Ordering[Option[Int]]) = {
  import ord._
  l < r
}

scala> cmpSome(Some(0), Some(1))
res2: Boolean = true
  

Комментарии:

1. На самом деле это не отвечает на вопрос, почему реализация Ordered[T] волшебным образом приводит к экземпляру Ordering[T] . И почему вы должны создавать метод для выполнения последнего сравнения?

2. Я создал метод, чтобы я мог получить доступ к экземпляру упорядочения. Я импортирую содержимое этого экземпляра, чтобы получить его неявное преобразование в an Ops , которое определяет некоторые операторы сравнения. Используя тот же метод, я, наконец, точно выяснил, где находится неявное преобразование для an Ordered . scala-lang.org/api/current /…

3. Но это кажется довольно большой работой только для того, чтобы иметь возможность сравнивать два варианта?

4. import ord._ это именно то, что я хотел

Ответ №4:

Чтобы ответить на ваш второй вопрос, почему вы не можете этого сделать: Some(2) > Some(1)

Вы можете, используя импорт и работая с Option[Int] , а не Some[Int] .

 @ import scala.math.Ordering.Implicits._ 
import scala.math.Ordering.Implicits._
@ Some(2) > Some(1) // doesn't work
cmd11.sc:1: value > is not a member of Some[Int]
val res11 = Some(2) > Some(1)
                    ^
Compilation Failed
@ (Some(2): Option[Int]) > (Some(1): Option[Int]) // Option[Int] works fine
res11: Boolean = true
@ Option(2) > Option(1) 
res12: Boolean = true
@ (None: Option[Int]) > (Some(1): Option[Int]) 
res13: Boolean = false
  

На практике ваши типы, вероятно, будут Option[Int] скорее, чем Some[Int] , так что это не будет так некрасиво, и вам не понадобится явное повышение.

Ответ №5:

Я полагаю, вы понимаете, почему сортировка не работает, когда вы не передаете порядок, и ни один из них не доступен в области видимости. Что касается того, почему функция сортировки работает, когда вы расширяете свой класс из упорядоченного признака. Ответ заключается в том, что при расширении из упорядоченного признака тип кода проверяется, поскольку признак содержит функцию типа <,> и т.д. Таким образом, нет необходимости выполнять неявное преобразование и, следовательно, нет жалоб на отсутствие неявного упорядочения.

Что касается вашего второго вопроса, Some(2) > Some(1) не будет работать, потому что Some не расширяет упорядоченный признак, и, похоже, в области видимости нет какой-либо неявной функции, которая неявно преобразует Some во что-то, имеющее функцию >

Ответ №6:

Спасибо за подробный вопрос с примерами.

Мой ответ основан на том, что я узнал из отличной статьи здесь: http://like-a-boss.net/2012/07/30/ordering-and-ordered-in-scala.html

Вся заслуга автора здесь.

Цитирование статьи:

Coming back to our Box example - the scala library defines an implicit conversion between Ordered[T] and Ordering[T] and vice-versa.

Сопутствующий объект Ordered в https://github.com/scala/scala/blob/2.12.x/src/library/scala/math/Ordered.scala обеспечивает требуемое преобразование здесь:

/** Lens from `Ordering[T]` to `Ordered[T]` */
implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }

Однако обратное преобразование не определено, и я не уверен, почему?