Basit bir vaka sınıfı için Siparişi tanımlamanın kolay deyimsel yolu


110

Basit scala durum sınıfı örneklerinin bir listesine sahibim ve bunları kullanarak öngörülebilir, sözlüksel sırayla yazdırmak istiyorum list.sorted , ancak "... için örtük Sıralama tanımlanmadı" .

Vaka sınıfları için sözlüksel sıralama sağlayan bir örtük var mı?

Sözlüksel sıralamayı vaka sınıfına dahil etmenin basit deyimsel bir yolu var mı?

scala> case class A(tag:String, load:Int)
scala> val l = List(A("words",50),A("article",2),A("lines",7))

scala> l.sorted.foreach(println)
<console>:11: error: No implicit Ordering defined for A.
          l.sorted.foreach(println)
            ^

Bir 'hack''ten memnun değilim:

scala> l.map(_.toString).sorted.foreach(println)
A(article,2)
A(lines,7)
A(words,50)

8
Burada birkaç genel çözüm içeren bir blog yazısı yazdım .
Travis Brown

Yanıtlar:


152

Benim kişisel favori yöntemim, açık, özlü ve doğru olduğu için Tuples için sağlanan örtük sıralamayı kullanmaktır:

case class A(tag: String, load: Int) extends Ordered[A] {
  // Required as of Scala 2.11 for reasons unknown - the companion to Ordered
  // should already be in implicit scope
  import scala.math.Ordered.orderingToOrdered

  def compare(that: A): Int = (this.tag, this.load) compare (that.tag, that.load)
}

Bunun nedeni çalışır için arkadaşıOrdered bir örtük dönüştürme gelen tanımlayıp Ordering[T]için Ordered[T]hangi uygulayan herhangi bir sınıf için kapsam içindedir Ordered. Örtük varlığı Orderingiçin s Tuples bir dönüşüm sağlar TupleN[...]için Ordered[TupleN[...]]bir örtük sağlanan Ordering[TN]tüm unsurları için var T1, ..., TNhiçbir bir veri türüne tür hiçbir mantıklı nedeni her zaman böyle olması gerektiğini başlığın, Ordering.

Tuples için örtük sıralama, bir bileşik sıralama anahtarı içeren herhangi bir sıralama senaryosu için gitmeniz gereken yerdir:

as.sortBy(a => (a.tag, a.load))

Bu yanıtın popüler olduğu kanıtlandığı için, aşağıdakilere benzer bir çözümün bazı durumlarda kurumsal sınıf ™ olarak kabul edilebileceğini belirterek, konuyu genişletmek istiyorum:

case class Employee(id: Int, firstName: String, lastName: String)

object Employee {
  // Note that because `Ordering[A]` is not contravariant, the declaration
  // must be type-parametrized in the event that you want the implicit
  // ordering to apply to subclasses of `Employee`.
  implicit def orderingByName[A <: Employee]: Ordering[A] =
    Ordering.by(e => (e.lastName, e.firstName))

  val orderingById: Ordering[Employee] = Ordering.by(e => e.id)
}

Verilen es: SeqLike[Employee], es.sorted()ada es.sorted(Employee.orderingById)göre sıralar ve kimliğe göre sıralar. Bunun birkaç faydası vardır:

  • Sıralar, görünür kod yapıları olarak tek bir konumda tanımlanır. Bu, birçok alanda karmaşık sıralamalarınız varsa kullanışlıdır.
  • Scala kitaplığında uygulanan çoğu sıralama işlevi Ordering, örneklerini kullanarak çalışır , bu nedenle bir sıralama sağlamak çoğu durumda örtük bir dönüşümü doğrudan ortadan kaldırır.

Örneğiniz harika! Tek astarlı ve varsayılan siparişim var. Çok teşekkür ederim.
ya_pulser

7
Cevapta önerilen durum sınıfı A, 2.10 ölçeğinde derlenmiyor gibi görünüyor. Bir şey mi kaçırıyorum?
Doron Yaacoby

3
@DoronYaacoby: Ayrıca bir hata alıyorum value compare is not a member of (String, Int).
bluenote10

1
@JCracknell Hata, içe aktarmadan sonra bile hala orada (Scala 2.10.4). Derleme sırasında hata oluşur, ancak IDE'de işaretlenmez. (ilginç bir şekilde, REPL'de düzgün çalışıyor). Bu soruna sahip olanlar için, bu SO cevabındaki çözüm işe yarar (yukarıdaki kadar zarif olmasa da). Hata ise, kimse rapor etti mi?
Jus12

2
DÜZELTME: Siparişin kapsamı içeri çekilmiyor, onu örtük olarak içeri çekebilirsiniz, ancak yalnızca Sıralamayı doğrudan Kullanmak yeterince kolaydır: def Compare (that: A) = Ordering.Tuple2 [String, String] .compare (tuple (this ), tuple (that))
brendon

46
object A {
  implicit val ord = Ordering.by(unapply)
}

Bu, A değiştiğinde otomatik olarak güncellenmesi avantajına sahiptir. Ancak, A'nın alanlarının sıralamanın onları kullanacağı sıraya göre yerleştirilmesi gerekir.


Harika görünüyor, ama bunu nasıl kullanacağımı <console>:12: error: not found: value unapply
çözemiyorum

29

Özetlemek gerekirse, bunu yapmanın üç yolu vardır:

  1. Tek seferlik sıralama için .sortBy yöntemini kullanın, @Shadowlands'ın gösterdiği gibi
  2. @ Keith'in dediği gibi, sıralamayı yeniden kullanmak için Case sınıfını Ordered trait ile genişletin.
  3. Özel bir sıralama tanımlayın. Bu çözümün avantajı, sıralamaları yeniden kullanabilmeniz ve aynı sınıftaki örnekleri sıralamak için birden çok yola sahip olmanızdır:

    case class A(tag:String, load:Int)
    
    object A {
      val lexicographicalOrdering = Ordering.by { foo: A => 
        foo.tag 
      }
    
      val loadOrdering = Ordering.by { foo: A => 
        foo.load 
      }
    }
    
    implicit val ord = A.lexicographicalOrdering 
    val l = List(A("words",1), A("article",2), A("lines",3)).sorted
    // List(A(article,2), A(lines,3), A(words,1))
    
    // now in some other scope
    implicit val ord = A.loadOrdering
    val l = List(A("words",1), A("article",2), A("lines",3)).sorted
    // List(A(words,1), A(article,2), A(lines,3))

Sorunuzu cevaplamak Scala'da List ((2,1), (1,2)) gibi sihir yapabilen herhangi bir standart fonksiyon var mıdır.

Önceden tanımlanmış bir dizi sıralama vardır , örneğin String için, 9'a kadar diziler vb.

Vaka sınıfları için böyle bir şey yoktur, çünkü alan adlarının a-priori bilinmediği (en azından makroların büyüsü olmadığı) ve vaka sınıfı alanlarına, isim / ürün yineleyiciyi kullanma.


Örnekler için çok teşekkür ederim. Örtük sıralamayı anlamaya çalışacağım.
ya_pulser

8

unapplyTamamlayıcı nesnesinin yöntemi için örnek sınıfından bir dönüşüm sağlar Option[Tuple], Tupledurum sınıfının ilk bağımsız listesine karşılık gelen başlık olur. Diğer bir deyişle:

case class Person(name : String, age : Int, email : String)

def sortPeople(people : List[Person]) = 
    people.sortBy(Person.unapply)

6

SortBy yöntemi bunu yapmanın tipik bir yoludur, örneğin ( tagalanda sırala ):

scala> l.sortBy(_.tag)foreach(println)
A(article,2)
A(lines,7)
A(words,50)

Vaka sınıfında 3+ alan olması durumunda ne yapmalı? l.sortBy( e => e._tag + " " + e._load + " " + ... )?
ya_pulser

Kullanılıyorsa sortBy, evet, ya öyle ya da sınıfa / sınıfa uygun bir işlev ekleyin / kullanın (örneğin _.toString, kendi sözlüksel olarak önemli özel yönteminiz veya harici işleviniz).
Shadowlands

Scala'da List((2,1),(1,2)).sortedkasa sınıfı nesneler gibi sihir yapabilen herhangi bir standart işlev var mı ? Adlandırılmış tuple'lar (case class == named tuple) ile basit tuple arasında büyük bir fark görmüyorum.
ya_pulser

Bu satırlardan en yakın olanı, bir almak için tamamlayıcı nesnenin uygulanmayan yöntemini kullanmak ve Option[TupleN]ardından şunu çağırmaktır get: l.sortBy(A.unapply(_).get)foreach(println)bu, karşılık gelen demet üzerinde sağlanan sıralamayı kullanır, ancak bu, yukarıda verdiğim genel fikrin açık bir örneğidir. .
Shadowlands

5

Bir vaka sınıfı kullandığınızdan , Ordered ile şu şekilde genişletebilirsiniz :

case class A(tag:String, load:Int) extends Ordered[A] { 
  def compare( a:A ) = tag.compareTo(a.tag) 
}

val ls = List( A("words",50), A("article",2), A("lines",7) )

ls.sorted
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.