Compare, Ordering i sortowanie po swojemu

0

Chciałbym uzyskać wygodną możliwość sortowania, taką jak za pomocą sortBy. Ale na początku zdefiniujmy sobie pewne rzeczy:

case class Dist(val i: Int) extends Ordered[Dist] { override def compare(b:Dist) = i compareTo b.i }
case class C (val a : Int, val b: Dist, val c: Int)
val l = for (i <- 1 to 2 ; j <- 1 to 2; k <- 1 to 2) yield new C(i,Dist(j),k)

Lista wygląda tak:
scala.collection.immutable.IndexedSeq[C] = Vector(C(1,Dist(1),1), C(1,Dist(1),2), C(1,Dist(2),1), C(1,Dist(2),2), C(2,Dist(1),1), C(2,Dist(1),2), C(2,Dist(2),1), C(2,Dist(2),2))
Mogę sobie ją wygodnie posortować

 l.sortBy(p=> (p.a,p.b,p.c))

Ale powiedzmy, że chce napisać sobie swoje sortowanie które bedzie działało podobnie zamiast korzystać z Tupli.
Chce je wywoływać mniej więcej tak:

implicit val MyOrdering = new OrderingOwn[C,_]({c => c.a}, {c=>c.b}, { c=> c.c})
l.sorted

To ma działać tak, że przekazuje kilka funkcji które mówią o porządku sortowania.
W ten sposób miałbym wygodne sortowanie do różnych klas. Wystarczyło by stworzyć odpowiedni obiekt typu OrderingOwn
Jest jednak problem, ponieważ nie wiem jak rozwiązać taką rzecz, że przekazywane przeze mnie funkcje zwracają wartości różnych typów. Oto moja klasa OrderingOwn:

class OrderingOwn[T, U <% Ordered[U]](val pola: Function1[T,U]*) extends Ordering[T] {

def compare (a: T, b:T) = {
 pola.dropWhile(p => p(a) == p(b)).headOption.map(p => p(a).compare(p(b))) match {
  case Some(i) => i
  case None => 0
 }
}
}

Dodam że gdy chce posortować po tym samym typie (np Intach) to nie ma problemu:

implicit val MyOrdering = new OrderingOwn[C,Int]({c => c.c}, { c=> c.a})

Pytanie jest takie: jak się uniezależnić od jednego typu i zrobić to aby obsługiwało różne (wszystkie implementujące Ordered) ?

1

Jest coś takiego jak HList czyli lista heterogeniczna, a implementacja w Scali jest np w bibliotece shapeless. Prawdopodobnie to mogłoby być pomocne, choć zaraz muszę spadać i nie zastanowiłem się nad tym dobrze.

PS:
Pewnego rodzaju opcją jest też zrobienie 22 konstruktorów w klasie OrderingOwn, każdy dla innej liczby funkcji wejściowych :]

edit:
Chociaż kurcze teraz problem wydaje się trudniejszy niż wcześniej.

1

Udało mi się coś takiego zaklepać:

import scala.collection.mutable.ListBuffer

case class Dist(i: Int) extends Ordered[Dist] {
  override def compare(b: Dist) = i compareTo b.i
}

case class C(a: Int, b: Dist, c: Char)

class FieldComparator[A] extends Ordering[A] {
  private val funs: ListBuffer[(A, A) => Int] = ListBuffer()

  def compare(a: A, b: A) = funs.view.map(f => f(a, b)).find(0 !=).getOrElse(0)

  def on[B <% Ordered[B]](f: A => B) = {
    funs += ((a: A, b: A) => f(a).compare(f(b)))
    this
  }
}

object Test extends App {
  val l = for (i <- 1 to 2; j <- 1 to 2; k <- 'a' to 'c') yield
    new C(i, Dist(j), k)
  //implicit val MyOrdering = new FieldComparator[C].on(_.b) // doesn't work
  implicit val MyOrdering = new FieldComparator[C].on((c: C) => c.b)
    .on((c: C) => c.c).on((c: C) => c.a)
  println(l.sorted)
}

Niestety zakomentowany kod się nie kompiluje, a wyglądałby dużo lepiej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1