#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
, которое определяет некоторые операторы сравнения. Используя тот же метод, я, наконец, точно выяснил, где находится неявное преобразование для anOrdered
. 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) }
Однако обратное преобразование не определено, и я не уверен, почему?