एक साधारण केस क्लास के लिए ऑर्डरिंग को परिभाषित करने का आसान मुहावरेदार तरीका


110

मेरे पास साधारण स्केला केस क्लास इंस्टेंसेस की एक सूची है और मैं उनका उपयोग करके पूर्वानुमेय, लेक्सियोग्राफिकल ऑर्डर में प्रिंट करना चाहता हूं list.sorted, लेकिन "कोई निहित आदेश नहीं है ..." प्राप्त करें।

क्या कोई ऐसा निहितार्थ है जो केस कक्षाओं के लिए शाब्दिक आदेश प्रदान करता है?

क्या केस क्लास में मिक्स-इन लेक्सिकोग्राफ़िक ऑर्डर करने का सरल मुहावरेदार तरीका है?

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)
            ^

मैं 'हैक' से खुश नहीं हूं:

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

8
मैंने अभी कुछ ब्लॉग यहाँ जेनेरिक समाधानों के साथ लिखे हैं
ट्रैविस ब्राउन

जवाबों:


152

मेरी व्यक्तिगत पसंदीदा विधि टुपल्स के लिए प्रदान किए गए निहित आदेश का उपयोग करना है, क्योंकि यह स्पष्ट, संक्षिप्त और सही है:

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)
}

यह काम करता है क्योंकि साथीOrdered एक अंतर्निहित रूपांतरण को परिभाषित करता Ordering[T]है Ordered[T]जिसमें से किसी भी वर्ग के कार्यान्वयन के लिए गुंजाइश है OrderedOrderingएस के लिए निहित एस का अस्तित्व Tupleएक रूपांतरण प्रदान TupleN[...]करने में सक्षम बनाता है Ordered[TupleN[...]]बशर्ते कि टपल के Ordering[TN]सभी तत्वों T1, ..., TNके लिए एक निहित मौजूद है , जो हमेशा होना चाहिए क्योंकि यह बिना किसी डेटा प्रकार के सॉर्ट करने के लिए कोई मतलब नहीं है Ordering

टुपल्स के लिए निहित आदेश आपके सॉर्ट करने के लिए किसी भी प्रकार के परिदृश्य के लिए एक समग्र सॉर्ट कुंजी है:

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

जैसा कि यह जवाब लोकप्रिय साबित हुआ है कि मैं इस पर विस्तार करना चाहूंगा, यह देखते हुए कि कुछ परिस्थितियों में निम्नलिखित के जैसा एक समाधान उद्यम-ग्रेड ™ माना जा सकता है:

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)
}

दिया es: SeqLike[Employee], es.sorted()नाम से छाँटेगा, और es.sorted(Employee.orderingById)आईडी से छाँटेगा। इसके कुछ लाभ हैं:

  • छांटे दृश्य स्थान कलाकृतियों के रूप में एक ही स्थान में परिभाषित कर रहे हैं। यह उपयोगी है यदि आपके पास कई क्षेत्रों पर जटिल प्रकार हैं।
  • स्केला लाइब्रेरी में कार्यान्वित अधिकांश छँटाई कार्यक्षमता इंस्टेंसेस के उपयोग से संचालित होती है Ordering, इसलिए सीधे ऑर्डर देने से अधिकांश मामलों में निहित रूपांतरण समाप्त हो जाता है।

आपका उदाहरण अद्भुत है! एकल-लाइनर और मेरे पास डिफ़ॉल्ट ऑर्डर है। आपका बहुत बहुत धन्यवाद।
हां_पुलसर

7
जवाब में सुझाई गई केस क्लास ए को स्कैला 2.10 के तहत संकलन नहीं लगता है। क्या मैं कुछ भूल रहा हूँ?
डोरोन याकोबी

3
@ डोरोनयैकोबी: मुझे भी एक त्रुटि मिलती है value compare is not a member of (String, Int)
bluenote10

1
@JCracknell आयात के बाद भी त्रुटि बनी हुई है (Scala 2.10.4)। संकलन के दौरान त्रुटि होती है, लेकिन IDE में ध्वजांकित नहीं होता है। (दिलचस्प है, यह आरईपीएल में ठीक से काम करता है)। जिन लोगों को यह समस्या है, उनके लिए इस एसओ जवाब में समाधान काम करता है (हालांकि ऊपर जैसा सुरुचिपूर्ण नहीं है)। यदि यह बग है, तो क्या किसी ने इसकी सूचना दी है?
जुस 12

2
FIX: ऑर्डर करने का दायरा नहीं खींचा जा रहा है, आप इसे आसानी से खींच सकते हैं, लेकिन यह केवल ऑर्डर करने के लिए सीधे उपयोग करने के लिए पर्याप्त आसान है: डिफ तुलना (कि: ए) = ऑर्डरिंग .uple2 [स्ट्रिंग, स्ट्रिंग] .compare (tuple (यह) ), टपल (())
ब्रेंडन

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

इसका लाभ यह है कि जब भी कोई परिवर्तन होता है तो यह स्वचालित रूप से अपडेट हो जाता है। लेकिन, ए के क्षेत्रों को उस क्रम में रखा जाना चाहिए जिसके द्वारा आदेश उनका उपयोग करेगा।


लगता है, शांत, लेकिन मैं यह कैसे उपयोग करने के लिए समझ नहीं पा रहा हूँ, मुझे मिलता है:<console>:12: error: not found: value unapply
zbstof

29

संक्षेप में, ऐसा करने के तीन तरीके हैं:

  1. @ -Shadowlands के रूप में एक-बंद छँटाई के लिए .sortBy विधि का उपयोग करें
  2. @Keith ने कहा, ऑर्डर किए गए विशेषता के साथ केस क्लास को छांटने के लिए।
  3. एक कस्टम ऑर्डर को परिभाषित करें। इस समाधान का लाभ यह है कि आप आदेशों का पुन: उपयोग कर सकते हैं और एक ही वर्ग के उदाहरणों को क्रमबद्ध करने के कई तरीके हैं:

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

आपके प्रश्न का उत्तर देते हुए क्या कोई मानक फ़ंक्शन शामिल किया गया है, जो सूची (२,१), (२) की तरह जादू कर सकता है।

पूर्वनिर्धारित आदेशों का एक सेट है , उदाहरण के लिए स्ट्रिंग के लिए, 9 एरिटी तक ट्यूपल्स और इसी तरह।

केस क्लासेस के लिए ऐसी कोई चीज मौजूद नहीं है, क्योंकि इसे रोल करना आसान बात नहीं है, यह देखते हुए कि फील्ड नामों को ए-प्रायरी (कम से कम मैक्रोज़ मैजिक के बिना) नहीं जाना जाता है और आप केस क्लास फील्ड्स को इसके अलावा किसी अन्य तरीके से एक्सेस नहीं कर सकते हैं। नाम / उत्पाद का उपयोग करने वाला।


उदाहरण के लिए बहुत बहुत धन्यवाद। मैं निहित आदेश को समझने की कोशिश करूंगा।
ya_pulser

8

unapplyसाथी ऑब्जेक्ट की विधि आपके केस क्लास से ए में रूपांतरण प्रदान करती है Option[Tuple], जहां केस क्लास Tupleकी पहली तर्क सूची के अनुरूप ट्यूपल है। दूसरे शब्दों में:

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

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

6

सॉर्टबी विधि ऐसा करने का एक विशिष्ट तरीका होगा, जैसे ( tagफ़ील्ड पर सॉर्ट ):

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

केस क्लास में 3+ फील्ड के मामले में क्या करना है? l.sortBy( e => e._tag + " " + e._load + " " + ... )?
हां_पुलसर

यदि उपयोग कर रहे हैं sortBy, तो हाँ, या तो, या कक्षा में / (जैसे _.toString, या अपने स्वयं के महत्वपूर्ण-महत्वपूर्ण कस्टम विधि या बाहरी फ़ंक्शन) के लिए एक उपयुक्त फ़ंक्शन जोड़ें / उपयोग करें ।
शैडोलैंड

क्या स्केला में कोई मानक कार्य शामिल List((2,1),(1,2)).sortedहै जो केस क्लास ऑब्जेक्ट की तरह जादू कर सकता है ? मुझे नामित टुपल्स (केस क्लास == टुपल नाम) और सरल टुपल्स के बीच कोई बड़ा अंतर नहीं दिखता है।
ya_pulser

निकटतम मैं उन पंक्तियों के साथ प्राप्त कर सकता हूं जो किसी को प्राप्त करने के लिए साथी वस्तु की अनुपयोगी विधि का उपयोग करना है Option[TupleN], फिर getउस पर कॉल करें : l.sortBy(A.unapply(_).get)foreach(println)जो संबंधित टपल पर दिए गए आदेश का उपयोग करता है, लेकिन यह सामान्य विचार का एक स्पष्ट उदाहरण है जो मैं ऊपर देता हूं। ।
शैडोलैंड्स

5

चूँकि आपने एक केस क्लास का उपयोग किया था, जिसे आप इस तरह से ऑर्डर कर सकते थे :

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.