मैं स्केल-संग्रह में समृद्ध-मेरी-लाइब्रेरी पैटर्न कैसे लागू करूं?


92

सबसे शक्तिशाली स्काला में उपलब्ध पैटर्न में से एक समृद्ध-मेरी-पुस्तकालय * पैटर्न है, जो निहित रूपांतरण का उपयोग करता है दिखाई गतिशील विधि संकल्प की आवश्यकता के बिना मौजूदा वर्गों के लिए तरीकों को जोड़ने के लिए। उदाहरण के लिए, यदि हम चाहते हैं कि सभी तारों में वह विधि हो spacesजो गिनाए कि उनके पास कितने व्हाट्सएप वर्ण हैं, तो हम कर सकते हैं:

class SpaceCounter(s: String) {
  def spaces = s.count(_.isWhitespace)
}
implicit def string_counts_spaces(s: String) = new SpaceCounter(s)

scala> "How many spaces do I have?".spaces
res1: Int = 5

दुर्भाग्यवश, जेनेरिक कलेक्शन से निपटने पर यह पैटर्न मुश्किल में पड़ जाता है। उदाहरण के लिए, संग्रह के साथ क्रमिक रूप से आइटम समूहीकरण के बारे में कई प्रश्न पूछे गए हैं । एक शॉट में उस काम में कुछ भी नहीं बनाया गया है, इसलिए यह सामान्य संग्रह Cऔर सामान्य तत्व प्रकार का उपयोग करके समृद्ध-मेरी-लाइब्रेरी पैटर्न के लिए एक आदर्श उम्मीदवार लगता है A:

class SequentiallyGroupingCollection[A, C[A] <: Seq[A]](ca: C[A]) {
  def groupIdentical: C[C[A]] = {
    if (ca.isEmpty) C.empty[C[A]]
    else {
      val first = ca.head
      val (same,rest) = ca.span(_ == first)
      same +: (new SequentiallyGroupingCollection(rest)).groupIdentical
    }
  }
}

को छोड़कर, ज़ाहिर है, यह काम नहीं करता है । REPL हमें बताता है:

<console>:12: error: not found: value C
               if (ca.isEmpty) C.empty[C[A]]
                               ^
<console>:16: error: type mismatch;
 found   : Seq[Seq[A]]
 required: C[C[A]]
                 same +: (new SequentiallyGroupingCollection(rest)).groupIdentical
                      ^

दो समस्याएं हैं: हम C[C[A]]खाली C[A]सूची (या पतली हवा) से कैसे प्राप्त करते हैं ? और हम एक के बजाय लाइन C[C[A]]से वापस कैसे प्राप्त करें ?same +:Seq[Seq[A]]

* पूर्व में पिंप-माय-लाइब्रेरी के रूप में जाना जाता था।


1
बड़ा अच्छा सवाल! और, और भी बेहतर, यह एक जवाब के साथ आता है! :-)
डैनियल सी। सोबरल

2
@ डैनियल - मुझे इसमें दो या दो से अधिक उत्तरों के साथ आने पर कोई आपत्ति नहीं है!
रेक्स केर

2
भूल जाओ, दोस्त। जब भी मुझे ऐसा कुछ करने की आवश्यकता होती है, मैं इसे देखने के लिए इसे बुकमार्क कर रहा हूं। :-)
डैनियल सी। सोबरल

जवाबों:


74

इस समस्या को समझने की कुंजी यह महसूस करना है कि संग्रह पुस्तकालय में संग्रह के साथ निर्माण और काम करने के दो अलग-अलग तरीके हैं । एक अपने सभी अच्छे तरीकों के साथ सार्वजनिक संग्रह इंटरफ़ेस है। अन्य, जो संग्रह पुस्तकालय बनाने में बड़े पैमाने पर उपयोग किया जाता है , लेकिन जो इसके बाहर कभी उपयोग नहीं किए जाते हैं, वे बिल्डरों हैं।

समृद्ध करने में हमारी समस्या ठीक वैसी ही है जैसी संग्रह लाइब्रेरी का सामना करना पड़ता है जब उसी प्रकार के संग्रह को वापस करने की कोशिश की जाती है। यही है, हम संग्रह बनाना चाहते हैं, लेकिन जब हम उदारतापूर्वक काम करते हैं, तो हमारे पास "उसी प्रकार को संदर्भित करने का एक तरीका नहीं है जो संग्रह पहले से ही है"। इसलिए हमें बिल्डरों की जरूरत है ।

अब सवाल यह है कि हम अपने बिल्डरों को कहां से लाएं? स्पष्ट स्थान संग्रह से ही है। यह काम नहीं करता है । हमने पहले से ही तय कर लिया था, एक सामान्य संग्रह की ओर बढ़ते हुए, कि हम संग्रह के प्रकार को भूलते जा रहे हैं। इसलिए भले ही संग्रह एक बिल्डर को वापस कर सकता है जो उस प्रकार के अधिक संग्रह उत्पन्न करेगा जो हम चाहते हैं, यह नहीं जानता होगा कि प्रकार क्या था।

इसके बजाय, हम अपने बिल्डरों को उन CanBuildFromइम्प्लिट्स से प्राप्त करते हैं जो चारों ओर तैर रहे हैं। ये विशेष रूप से इनपुट और आउटपुट प्रकारों के मिलान और आपको उचित रूप से टाइप किए गए बिल्डर को देने के उद्देश्य से मौजूद हैं।

तो, हमारे पास बनाने के लिए दो वैचारिक छलांग हैं:

  1. हम मानक संग्रह संचालन का उपयोग नहीं कर रहे हैं, हम बिल्डरों का उपयोग कर रहे हैं।
  2. हम इन बिल्डरों को निहितार्थ से प्राप्त करते हैं CanBuildFrom, न कि सीधे हमारे संग्रह से।

आइए एक उदाहरण देखें।

class GroupingCollection[A, C[A] <: Iterable[A]](ca: C[A]) {
  import collection.generic.CanBuildFrom
  def groupedWhile(p: (A,A) => Boolean)(
    implicit cbfcc: CanBuildFrom[C[A],C[A],C[C[A]]], cbfc: CanBuildFrom[C[A],A,C[A]]
  ): C[C[A]] = {
    val it = ca.iterator
    val cca = cbfcc()
    if (!it.hasNext) cca.result
    else {
      val as = cbfc()
      var olda = it.next
      as += olda
      while (it.hasNext) {
        val a = it.next
        if (p(olda,a)) as += a
        else { cca += as.result; as.clear; as += a }
        olda = a
      }
      cca += as.result
    }
    cca.result
  }
}
implicit def iterable_has_grouping[A, C[A] <: Iterable[A]](ca: C[A]) = {
  new GroupingCollection[A,C](ca)
}

चलिए इसे अलग लेते हैं। सबसे पहले, संग्रह-के-संग्रह के निर्माण के लिए, हम जानते हैं कि हमें दो प्रकार के संग्रह बनाने की आवश्यकता होगी: C[A]प्रत्येक समूह के लिए, और C[C[A]]वह सभी समूहों को एक साथ इकट्ठा करता है। इस प्रकार, हमें दो बिल्डरों की आवश्यकता है, एक जो Aएस लेता है और C[A]एस बनाता है , और एक जो C[A]एस लेता है और C[C[A]]एस बनाता है । के प्रकार को CanBuildFromदेखते हुए, हम देखते हैं

CanBuildFrom[-From, -Elem, +To]

जिसका अर्थ है कि CanBuildFrom हमारे संग्रह के प्रकार को जानना चाहता है - हमारे मामले में, यह C[A], और फिर जनरेट किए गए संग्रह के तत्व और उस संग्रह का प्रकार। इसलिए हम उन्हें निहित मापदंडों के रूप में भरते हैंcbfcc और cbfc

यह महसूस करने के बाद, यह सबसे काम है। हम CanBuildFromबिल्डरों को देने के लिए अपने एस का उपयोग कर सकते हैं (आपको केवल उन्हें लागू करने की आवश्यकता है)। और एक बिल्डर के साथ एक संग्रह का निर्माण कर सकते हैं +=, इसे उस संग्रह में परिवर्तित कर सकते हैं जिसे अंततः माना जाता है result, और खुद को खाली करना और फिर से शुरू करने के लिए तैयार होना चाहिए clear। बिल्डरों को खाली करना शुरू हो जाता है, जो हमारी पहली संकलन त्रुटि को हल करता है, और चूंकि हम बिल्डरों का उपयोग पुनरावृत्ति के बजाय कर रहे हैं, दूसरी त्रुटि भी दूर हो जाती है।

एक अंतिम थोड़ा विस्तार - एल्गोरिथ्म के अलावा जो वास्तव में काम करता है - निहित रूपांतरण में है। ध्यान दें कि हम उपयोग new GroupingCollection[A,C]नहीं करते हैं [A,C[A]]। इसका कारण यह है कि वर्ग की घोषणा Cएक पैरामीटर के साथ थी , जिसे वह इसे स्वयं Aपास करता है। तो हम बस इसे टाइप करते हैंC , और इसे बनाने की अनुमति देते हैं C[A]। यदि आप दूसरा तरीका आजमाते हैं, तो माइनर डिटेल, लेकिन आपको कंपाइल-टाइम त्रुटियाँ मिलेंगी।

यहां, मैंने विधि को "समान तत्वों" संग्रह की तुलना में थोड़ा अधिक सामान्य बना दिया है - बल्कि, जब भी अनुक्रमिक तत्वों का परीक्षण विफल हो जाता है, तो विधि मूल संग्रह को काट देती है।

आइये देखते हैं हमारी विधि:

scala> List(1,2,2,2,3,4,4,4,5,5,1,1,1,2).groupedWhile(_ == _)
res0: List[List[Int]] = List(List(1), List(2, 2, 2), List(3), List(4, 4, 4), 
                             List(5, 5), List(1, 1, 1), List(2))

scala> Vector(1,2,3,4,1,2,3,1,2,1).groupedWhile(_ < _)
res1: scala.collection.immutable.Vector[scala.collection.immutable.Vector[Int]] =
  Vector(Vector(1, 2, 3, 4), Vector(1, 2, 3), Vector(1, 2), Vector(1))

यह काम करता हैं!

एकमात्र समस्या यह है कि हम आम तौर पर सरणियों के लिए ये तरीके उपलब्ध नहीं हैं, क्योंकि इसके लिए एक पंक्ति में दो निहित रूपांतरणों की आवश्यकता होगी। इसके आसपास आने के कई तरीके हैं, जिसमें एरेज़ के लिए अलग-अलग निहित रूपांतरण लिखना, कास्टिंग करना WrappedArray, और इसी तरह शामिल हैं।


संपादित करें: सरणियों और तारों से निपटने के लिए मेरा पसंदीदा दृष्टिकोण और इस तरह कोड को और भी अधिक सामान्य बनाना है और फिर उन्हें फिर से इस तरह से और अधिक विशिष्ट बनाने के लिए उपयुक्त निहितार्थ रूपांतरण का उपयोग करना है जो सरणियाँ भी काम करती हैं। इस विशेष मामले में:

class GroupingCollection[A, C, D[C]](ca: C)(
  implicit c2i: C => Iterable[A],
           cbf: CanBuildFrom[C,C,D[C]],
           cbfi: CanBuildFrom[C,A,C]
) {
  def groupedWhile(p: (A,A) => Boolean): D[C] = {
    val it = c2i(ca).iterator
    val cca = cbf()
    if (!it.hasNext) cca.result
    else {
      val as = cbfi()
      var olda = it.next
      as += olda
      while (it.hasNext) {
        val a = it.next
        if (p(olda,a)) as += a
        else { cca += as.result; as.clear; as += a }
        olda = a
      }
      cca += as.result
    }
    cca.result
  }
}

यहाँ हम एक अंतर्निहित जोड़ दिया है हमें एक देता है कि Iterable[A]से C--FOr सबसे संग्रह यह सिर्फ पहचान होगी (जैसे List[A]पहले से ही एक है Iterable[A]), लेकिन सरणियों के लिए यह एक वास्तविक अंतर्निहित रूपांतरण किया जाएगा। और, फलस्वरूप, हमने इस आवश्यकता को छोड़ दिया है कि - C[A] <: Iterable[A]हमने मूल रूप से केवल <%स्पष्ट करने के लिए आवश्यकता को पूरा किया है , इसलिए हम कंपाइलर को हमारे लिए भरने के बजाय इसका स्पष्ट रूप से उपयोग कर सकते हैं। इसके अलावा, हमने इस प्रतिबंध को शिथिल कर दिया है कि हमारा संग्रह-ऑफ-कलेक्शन है - C[C[A]]यह कोई भी है D[C], जिसे हम बाद में भरना चाहते हैं। क्योंकि हम इसे बाद में भरने जा रहे हैं, इसलिए हमने इसे विधि के स्तर के बजाय कक्षा स्तर तक धकेल दिया है। अन्यथा, यह मूल रूप से एक ही है।

अब सवाल यह है कि इसका उपयोग कैसे किया जाए। नियमित संग्रह के लिए, हम कर सकते हैं:

implicit def collections_have_grouping[A, C[A]](ca: C[A])(
  implicit c2i: C[A] => Iterable[A],
           cbf: CanBuildFrom[C[A],C[A],C[C[A]]],
           cbfi: CanBuildFrom[C[A],A,C[A]]
) = {
  new GroupingCollection[A,C[A],C](ca)(c2i, cbf, cbfi)
}

अब हम कहाँ और किस C[A]लिए प्लग इन करते हैं । ध्यान दें कि हमें कॉल पर स्पष्ट सामान्य प्रकारों की आवश्यकता है ताकि यह सीधे रख सके कि किस प्रकार के अनुरूप हैं। इसके लिए धन्यवाद , यह स्वचालित रूप से सरणियों को संभालता है।CC[C[A]]D[C]new GroupingCollectionimplicit c2i: C[A] => Iterable[A]

लेकिन रुको, अगर हम तार का उपयोग करना चाहते हैं तो क्या होगा? अब हम मुसीबत में हैं, क्योंकि आपके पास "तार का तार" नहीं हो सकता है। यह वह जगह है जहाँ अतिरिक्त अमूर्त मदद करता है: हम Dकुछ ऐसा कह सकते हैं जो तार को पकड़ने के लिए उपयुक्त है। चलो लेने Vector, और निम्नलिखित करें:

val vector_string_builder = (
  new CanBuildFrom[String, String, Vector[String]] {
    def apply() = Vector.newBuilder[String]
    def apply(from: String) = this.apply()
  }
)

implicit def strings_have_grouping(s: String)(
  implicit c2i: String => Iterable[Char],
           cbfi: CanBuildFrom[String,Char,String]
) = {
  new GroupingCollection[Char,String,Vector](s)(
    c2i, vector_string_builder, cbfi
  )
}

हमें CanBuildFromएक वेक्टर के निर्माण को संभालने के लिए एक नया चाहिए (लेकिन यह वास्तव में आसान है, क्योंकि हमें बस कॉल करने की आवश्यकता है Vector.newBuilder[String]), और फिर हमें सभी प्रकारों को भरने की आवश्यकता है ताकि GroupingCollectionसमझदारी से टाइप किया जाए। ध्यान दें कि हम पहले से ही [String,Char,String]CanBuildFrom के चारों ओर तैर रहे हैं , इसलिए तार को संग्रह से बनाया जा सकता है।

आइये इसे आजमाते हैं:

scala> List(true,false,true,true,true).groupedWhile(_ == _)
res1: List[List[Boolean]] = List(List(true), List(false), List(true, true, true))

scala> Array(1,2,5,3,5,6,7,4,1).groupedWhile(_ <= _) 
res2: Array[Array[Int]] = Array(Array(1, 2, 5), Array(3, 5, 6, 7), Array(4), Array(1))

scala> "Hello there!!".groupedWhile(_.isLetter == _.isLetter)
res3: Vector[String] = Vector(Hello,  , there, !!)

आप सरणियों के लिए समर्थन जोड़ने के लिए <% का उपयोग कर सकते हैं।
बेनामी

@ अनाम - एक ऐसा संदेह होता। लेकिन क्या आपने इस मामले में कोशिश की?
रेक्स केर

@Rex: "एक पंक्ति में दो निहित रूपांतरणों की आवश्यकता है" मुझे stackoverflow.com/questions/5332801/… की याद दिलाता है?
पीटर शमित्ज़

@Peter - संभवतः! मैं, हालांकि%% पर निर्भर होने के बजाय स्पष्ट निहितार्थ रूपांतरण लिखना चाहता हूं।
रेक्स केर

@Peters टिप्पणी के आधार पर मैंने सरणियों के लिए एक और अंतर्निहित रूपांतरण जोड़ने की कोशिश की, लेकिन मैं असफल रहा। मैं वास्तव में समझ नहीं पाया कि दृश्य सीमा को कहां जोड़ा जाए। @Rex, क्या आप कृपया अपना उत्तर संपादित कर सकते हैं और बता सकते हैं कि सरणियों के साथ काम करने के लिए कोड कैसे प्राप्त करें?
किरित्सुकु

29

के रूप में इस के लिए प्रतिबद्ध यह एक बहुत "समृद्ध" स्काला संग्रह करने के लिए आसान की तुलना में यह था जब रेक्स अपने उत्कृष्ट जवाब दिया है। साधारण मामलों के लिए यह इस तरह दिख सकता है,

import scala.collection.generic.{ CanBuildFrom, FromRepr, HasElem }
import language.implicitConversions

class FilterMapImpl[A, Repr](val r : Repr)(implicit hasElem : HasElem[Repr, A]) {
  def filterMap[B, That](f : A => Option[B])
    (implicit cbf : CanBuildFrom[Repr, B, That]) : That = r.flatMap(f(_).toSeq)
}

implicit def filterMap[Repr : FromRepr](r : Repr) = new FilterMapImpl(r)

जो filterMapसभी GenTraversableLikeएस के लिए एक "समान परिणाम प्रकार" सम्मान ऑपरेशन जोड़ता है ,

scala> val l = List(1, 2, 3, 4, 5)
l: List[Int] = List(1, 2, 3, 4, 5)

scala> l.filterMap(i => if(i % 2 == 0) Some(i) else None)
res0: List[Int] = List(2, 4)

scala> val a = Array(1, 2, 3, 4, 5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> a.filterMap(i => if(i % 2 == 0) Some(i) else None)
res1: Array[Int] = Array(2, 4)

scala> val s = "Hello World"
s: String = Hello World

scala> s.filterMap(c => if(c >= 'A' && c <= 'Z') Some(c) else None)
res2: String = HW

और सवाल से उदाहरण के लिए, समाधान अब जैसा दिखता है,

class GroupIdenticalImpl[A, Repr : FromRepr](val r: Repr)
  (implicit hasElem : HasElem[Repr, A]) {
  def groupIdentical[That](implicit cbf: CanBuildFrom[Repr,Repr,That]): That = {
    val builder = cbf(r)
    def group(r: Repr) : Unit = {
      val first = r.head
      val (same, rest) = r.span(_ == first)
      builder += same
      if(!rest.isEmpty)
        group(rest)
    }
    if(!r.isEmpty) group(r)
    builder.result
  }
}

implicit def groupIdentical[Repr : FromRepr](r: Repr) = new GroupIdenticalImpl(r)

नमूना REPL सत्र,

scala> val l = List(1, 1, 2, 2, 3, 3, 1, 1)
l: List[Int] = List(1, 1, 2, 2, 3, 3, 1, 1)

scala> l.groupIdentical
res0: List[List[Int]] = List(List(1, 1),List(2, 2),List(3, 3),List(1, 1))

scala> val a = Array(1, 1, 2, 2, 3, 3, 1, 1)
a: Array[Int] = Array(1, 1, 2, 2, 3, 3, 1, 1)

scala> a.groupIdentical
res1: Array[Array[Int]] = Array(Array(1, 1),Array(2, 2),Array(3, 3),Array(1, 1))

scala> val s = "11223311"
s: String = 11223311

scala> s.groupIdentical
res2: scala.collection.immutable.IndexedSeq[String] = Vector(11, 22, 33, 11)

फिर, ध्यान दें कि एक ही परिणाम प्रकार सिद्धांत को ठीक उसी तरह से देखा गया है जिस तरह से इसे groupIdenticalसीधे परिभाषित किया गया होगा GenTraversableLike


3
वाह! कर रहे हैं और भी अधिक जादुई टुकड़े इस तरह का ट्रैक रखने के, लेकिन वे सभी अच्छी तरह से गठबंधन! प्रत्येक गैर-संग्रह-पदानुक्रम संग्रह के बारे में चिंता न करना एक राहत की बात है।
रेक्स केर

3
मेरे एक-लाइन परिवर्तन को अस्वीकार करने के बाद से बहुत बुरा Iterator कृतज्ञतापूर्वक बाहर रखा गया है। "त्रुटि: प्रकार scala.collection.generic.FromRepr [Iterator [Int]] के सबूत पैरामीटर के लिए निहित मूल्य नहीं मिल सका"
psp

किस एक-पंक्ति को बदलने से इनकार कर दिया गया था?
माइल्स सबिन


2
मैं इसे गुरु में नहीं देखता; क्या यह वाष्पित हो गया, या 2.10.0 शाखा में समाप्त हो गया, या ...?
रेक्स केर

9

इस प्रतिबद्ध के रूप में के मैजिक का झुकाव उस समय से थोड़ा बदला हुआ है जब मीलों ने अपना उत्कृष्ट उत्तर दिया था।

निम्नलिखित कार्य करता है, लेकिन क्या यह विहित है? मुझे उम्मीद है कि तोपों में से एक इसे सही कर देगी। (या बल्कि, तोपों, बड़ी बंदूकों में से एक।) यदि दृश्य बाउंड ऊपरी सीमा है, तो आप ऐरे और स्ट्रिंग के लिए आवेदन खो देते हैं। यह प्रतीत नहीं होता है कि बाध्य GenTraversableLike या TraversableLike है या नहीं; लेकिन IsTraversableLike आपको GenTraversableLike देता है।

import language.implicitConversions
import scala.collection.{ GenTraversable=>GT, GenTraversableLike=>GTL, TraversableLike=>TL }
import scala.collection.generic.{ CanBuildFrom=>CBF, IsTraversableLike=>ITL }

class GroupIdenticalImpl[A, R <% GTL[_,R]](val r: GTL[A,R]) {
  def groupIdentical[That](implicit cbf: CBF[R, R, That]): That = {
    val builder = cbf(r.repr)
    def group(r: GTL[_,R]) {
      val first = r.head
      val (same, rest) = r.span(_ == first)
      builder += same
      if (!rest.isEmpty) group(rest)
    }
    if (!r.isEmpty) group(r)
    builder.result
  }
}

implicit def groupIdentical[A, R <% GTL[_,R]](r: R)(implicit fr: ITL[R]):
  GroupIdenticalImpl[fr.A, R] =
  new GroupIdenticalImpl(fr conversion r)

वहाँ एक से अधिक नौ त्वचा के साथ एक बिल्ली त्वचा के लिए रास्ता है। यह संस्करण कहता है कि एक बार जब मेरा स्रोत जेनट्रैवेसेबल लाइक में परिवर्तित हो जाता है, जब तक मैं जेनट्रैवेसेबल से परिणाम का निर्माण कर सकता हूं, बस ऐसा ही करें। मुझे अपने पुराने रीप में दिलचस्पी नहीं है।

class GroupIdenticalImpl[A, R](val r: GTL[A,R]) {
  def groupIdentical[That](implicit cbf: CBF[GT[A], GT[A], That]): That = {
    val builder = cbf(r.toTraversable)
    def group(r: GT[A]) {
      val first = r.head
      val (same, rest) = r.span(_ == first)
      builder += same
      if (!rest.isEmpty) group(rest)
    }
    if (!r.isEmpty) group(r.toTraversable)
    builder.result
  }
}

implicit def groupIdentical[A, R](r: R)(implicit fr: ITL[R]):
  GroupIdenticalImpl[fr.A, R] =
  new GroupIdenticalImpl(fr conversion r)

इस पहले प्रयास में Repr to GenTraversableLike का एक बदसूरत रूपांतरण शामिल है।

import language.implicitConversions
import scala.collection.{ GenTraversableLike }
import scala.collection.generic.{ CanBuildFrom, IsTraversableLike }

type GT[A, B] = GenTraversableLike[A, B]
type CBF[A, B, C] = CanBuildFrom[A, B, C]
type ITL[A] = IsTraversableLike[A]

class FilterMapImpl[A, Repr](val r: GenTraversableLike[A, Repr]) { 
  def filterMap[B, That](f: A => Option[B])(implicit cbf : CanBuildFrom[Repr, B, That]): That = 
    r.flatMap(f(_).toSeq)
} 

implicit def filterMap[A, Repr](r: Repr)(implicit fr: ITL[Repr]): FilterMapImpl[fr.A, Repr] = 
  new FilterMapImpl(fr conversion r)

class GroupIdenticalImpl[A, R](val r: GT[A,R])(implicit fr: ITL[R]) { 
  def groupIdentical[That](implicit cbf: CBF[R, R, That]): That = { 
    val builder = cbf(r.repr)
    def group(r0: R) { 
      val r = fr conversion r0
      val first = r.head
      val (same, other) = r.span(_ == first)
      builder += same
      val rest = fr conversion other
      if (!rest.isEmpty) group(rest.repr)
    } 
    if (!r.isEmpty) group(r.repr)
    builder.result
  } 
} 

implicit def groupIdentical[A, R](r: R)(implicit fr: ITL[R]):
  GroupIdenticalImpl[fr.A, R] = 
  new GroupIdenticalImpl(fr conversion r)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.