Compare, Ordering i sortowanie po swojemu

Compare, Ordering i sortowanie po swojemu
UB
  • Rejestracja: dni
  • Ostatnio: dni
0

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

Kopiuj
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ć

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

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

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

Kopiuj
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) ?

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
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.

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
1

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

Kopiuj
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.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.