Как отсортировать коллекцию списков в лексикографическом порядке в Scala?
Если A имеет свойство Ordered[A], я хотел бы иметь код, который работает следующим образом
val collection: List[List[A]] = ... // construct a list of lists of As
val sorted = collection sort { _ < _ }
И получить что-то, где списки были отсортированы в лексикографическом порядке. Конечно, только потому, что A имеет признак Ordered[A], не означает, что List[A] имеет Признак Ordered[List[A]]. Предположительно, однако, 'скала' способом сделать это-неявное определение.
Как я неявно преобразую a List[A] в A Ordered[List[A]], предполагая, что A имеет признак Ordered[A] (так что код выше просто работает)?
Я имею в виду использование лексикографического упорядочения на объектах
List[A], но мне бы хотелось код, который можно адаптировать к другим упорядочениям. 5 ответов:
Вдохновленный ответом Бена Линга, я написал свою собственную версию
sort:def sort[A : Ordering](coll: Seq[Iterable[A]]) = coll.sortedЧто эквивалентно:
Обратите внимание, чтоdef sort[A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A]) = coll.sortedorderingнеявно преобразуется вOrdering[Iterable[A]].Примеры:
scala> def sort[A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A]) = coll.sorted sort: [A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A])Seq[Iterable[A]] scala> val coll = List(List(1, 3), List(1, 2), List(0), Nil, List(2)) coll: List[List[Int]] = List(List(1, 3), List(1, 2), List(0), List(), List(2)) scala> sort(coll) res1: Seq[Iterable[Int]] = List(List(), List(0), List(1, 2), List(1, 3), List(2))Был задан вопрос, как снабдить вашу собственную функцию сравнения; достаточно использовать упорядочивание.fromLessThan:
scala> sort(coll)(Ordering.fromLessThan(_ > _)) res4: Seq[Iterable[Int]] = List(List(), List(2), List(1, 3), List(1, 2), List(0))Чтобы сделать пример, давайте определим оболочку Int, применяем
Ordering.byпозволяет сопоставить значение с другим типом, для которого уже существует экземпляр заказа. Учитывая, что также кортежи упорядочены, это может быть полезно для лексикографического сравнения классов падежей.Ordering.by(_.v), где_.vизвлекает базовое значение, и покажем, что мы получаем тот же результат:Наконец, давайте сделаем то же самое для класса case с большим количеством членов, повторно используя компараторы для кортежей:scala> case class Wrap(v: Int) defined class Wrap scala> val coll2 = coll.map(_.map(Wrap(_))) coll2: List[List[Wrap]] = List(List(Wrap(1), Wrap(3)), List(Wrap(1), Wrap(2)), List(Wrap(0)), List(), List(Wrap(2))) scala> sort(coll2)(Ordering.by(_.v)) res6: Seq[Iterable[Wrap]] = List(List(), List(Wrap(0)), List(Wrap(1), Wrap(2)), List(Wrap(1), Wrap(3)), List(Wrap(2)))scala> case class MyPair(a: Int, b: Int) defined class MyPair scala> val coll3 = coll.map(_.map(MyPair(_, 0))) coll3: List[List[MyPair]] = List(List(MyPair(1,0), MyPair(3,0)), List(MyPair(1,0), MyPair(2,0)), List(MyPair(0,0)), List(), List(MyPair(2,0))) scala> sort(coll3)(Ordering.by(x => (x.a, x.b))) res7: Seq[Iterable[MyPair]] = List(List(), List(MyPair(0,0)), List(MyPair(1,0), MyPair(2,0)), List(MyPair(1,0), MyPair(3,0)), List(MyPair(2,0)))
Вдохновленный ответом Бена Линга, я сумел придумать самый простой способ лексикографической сортировки списков: добавить строку
import scala.math.Ordering.Implicits._Перед выполнением сравнения списка[Int] убедитесь, что неявная функция
infixOrderingOpsнаходится в области видимости.
(11 минут назад я на самом деле не знал, как это сделать, я надеюсь, что это считается нормальным, чтобы ответить на мой собственный вопрос.)
implicit def List2OrderedList[A <% Ordered[A]](list1: List[A]): Ordered[List[A]] = { new Ordered[List[A]] { def compare(list2: List[A]): Int = { for((x,y) <- list1 zip list2) { val c = x compare y if(c != 0) return c } return list1.size - list2.size } } }Здесь важно отметить, что " вид связан'
A <% Ordered[A], что гарантирует, чтоAне нужно само по себеOrdered[A], просто есть способ сделать это преобразование. К счастью, объект библиотеки ScalaPredefимеет неявное преобразование изInts вRichInts, которые, в частности, являютсяOrdered[Int]s.Остальная часть кода просто реализует лексикографический заказ.
В 2.8, вы должны быть в состоянии просто сделать
collection.sorted.sortedпринимает неявный параметрOrdering. Любой тип, реализующийOrdered, имеет соответствующийOrdering(благодаря неявному преобразованиюOrdering.ordered). Существует также неявноеOrdering.Iterable, которое заставляетIterable[T]иметьOrdering, ЕслиTимеетOrdering.Однако, если вы попробуете это сделать, это не сработает:
scala> def sort[A <: Ordered[A]](coll: List[List[A]]) = coll.sorted <console>:5: error: could not find implicit value for parameter ord: Ordering[List[A]] def sort[A <: Ordered[A]](coll: List[List[A]]) = coll.sorted ^Вам нужно явно указать, что вы хотите
Ordering[Iterable[A]]:def sort[A <: Ordered[A]](coll: List[List[A]]) = coll.sorted[Iterable[A]]Я не уверен, почему компилятор не может найти
Ordering[Iterable[A]], Если тип элемента коллекции -List[A].
Вдохновленный комментарием Даниэля, вот рекурсивная версия:
implicit def toOrdered[A <% Ordered[A]](list1: List[A]): Ordered[List[A]] = { @scala.annotation.tailrec def c(list1:List[A], list2:List[A]): Int = { (list1, list2) match { case (Nil, Nil) => 0 case (x::xs, Nil) => 1 case (Nil, y::ys) => -1 case (x::xs, y::ys) => (x compare y) match { case 0 => c(xs, ys) case i => i } } } new Ordered[List[A]] { def compare(list2: List[A]): Int = c(list1, list2) } }В отношении комментария: Раньше я думал, что это скорее вопрос вкуса. Иногда легче проверить корректность рекурсивной функции, и, конечно, ваша версия достаточно коротка, чтобы не было убедительных причин предпочесть рекурсивную.
Тем не менее, я был заинтригован результатами работы. Поэтому я попытался сравнить его: см. http://gist.github.com/468435 . я был удивлен, увидев, что рекурсивная версия быстрее (при условии, что я сделал тест правильно). Результаты по-прежнему остаются верными для списка длиной около 10.
Comments