ฉันจะใช้รูปแบบการเพิ่มคุณค่า - ห้องสมุดของฉันกับคอลเลกชัน Scala ได้อย่างไร


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 +:เส้นแทนที่จะเป็น a ได้Seq[Seq[A]]อย่างไร?

* เดิมชื่อแมงดา - ของฉัน - ห้องสมุด


1
คำถามดีมาก! และที่ดีไปกว่านั้นคือมาพร้อมกับคำตอบ! :-)
Daniel C. Sobral

2
@ แดเนียล - ฉันไม่ขัดข้องที่จะมาพร้อมกับคำตอบสองคำตอบ!
Rex Kerr

2
ลืมไปเถอะเพื่อน ฉันบุ๊กมาร์กไว้เพื่อค้นหาเมื่อใดก็ตามที่ฉันต้องการทำสิ่งนี้ :-)
Daniel C. Sobral

คำตอบ:


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]s และหนึ่งที่ใช้เวลาC[A]และสร้างC[C[A]]s เมื่อมองไปที่ลายเซ็นของCanBuildFromเราจะเห็น

CanBuildFrom[-From, -Elem, +To]

ซึ่งหมายความว่า CanBuildFrom ต้องการทราบประเภทของคอลเลกชันที่เราเริ่มต้น - ในกรณีของเราคือC[A]จากนั้นองค์ประกอบของคอลเล็กชันที่สร้างขึ้นและประเภทของคอลเล็กชันนั้น ดังนั้นเรากรอกข้อมูลผู้ที่อยู่ในเป็นพารามิเตอร์โดยปริยายและcbfcccbfc

เมื่อตระหนักถึงสิ่งนี้นั่นคืองานส่วนใหญ่ เราสามารถใช้CanBuildFroms ของเราเพื่อให้เราสร้างผู้สร้าง (สิ่งที่คุณต้องทำคือใช้พวกเขา) และเป็นหนึ่งในผู้สร้างสามารถสร้างคอลเลกชันที่มีการ+=แปลงเป็นคอลเลกชันที่มันควรจะเป็นในท้ายที่สุดด้วยและล้างตัวเองและพร้อมที่จะเริ่มต้นอีกครั้งกับ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- สำหรับคอลเลกชันส่วนใหญ่สิ่งนี้จะเป็นข้อมูลประจำตัว (เช่น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]สำหรับCและสำหรับC[C[A]] D[C]โปรดทราบว่าเราต้องการประเภททั่วไปที่ชัดเจนในการโทรเพื่อnew GroupingCollectionให้สามารถระบุได้ว่าประเภทใดตรงกับอะไร ด้วยเหตุimplicit 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 Kerr

@Rex: "ต้องการการแปลงโดยนัยสองรายการติดต่อกัน" ทำให้ฉันนึกถึงstackoverflow.com/questions/5332801/…ใช้ได้ที่นี่หรือไม่
Peter Schmitz

@ ปีเตอร์ - อาจเป็นไปได้! ฉันมักจะเขียนการแปลงโดยนัยอย่างชัดเจนแทนที่จะอาศัย <% chaining แม้ว่า
Rex Kerr

จากความคิดเห็นของ @Peters ฉันพยายามเพิ่มการแปลงโดยนัยอื่นสำหรับอาร์เรย์ แต่ฉันล้มเหลว ฉันไม่เข้าใจว่าจะเพิ่มขอบเขตมุมมองที่ใด @Rex คุณช่วยแก้ไขคำตอบและแสดงวิธีรับโค้ดเพื่อทำงานกับอาร์เรย์ได้ไหม
kiritsuku

29

ด้วยเหตุนี้การ "เพิ่มประสิทธิภาพ" คอลเลกชัน Scala จะง่ายกว่าที่เคยเป็นมาเมื่อ Rex ให้คำตอบที่ยอดเยี่ยมของเขา สำหรับกรณีง่ายๆอาจมีลักษณะเช่นนี้

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)

อีกครั้งทราบว่าหลักการประเภทผลเดียวกันได้รับการปฏิบัติในทางเดียวกันว่าว่ามันจะได้รับการได้รับการกำหนดโดยตรงบนgroupIdenticalGenTraversableLike


3
เย้! ยังมีของวิเศษอีกมากมายให้ติดตามด้วยวิธีนี้ แต่ทั้งหมดนี้รวมเข้าด้วยกันอย่างลงตัว! การไม่ต้องกังวลเกี่ยวกับคอลเลกชันที่ไม่ใช่ลำดับชั้นของคอลเลกชันแต่ละรายการ
Rex Kerr

3
น่าเสียดายที่ Iterator ถูกแยกออกโดยไม่คิดค่าใช้จ่ายเนื่องจากการเปลี่ยนบรรทัดเดียวของฉันถูกปฏิเสธ "error: ไม่พบค่าโดยนัยสำหรับพารามิเตอร์หลักฐานประเภท scala.collection.generic.FromRepr [Iterator [Int]]"
psp

การเปลี่ยนบรรทัดใดที่ถูกปฏิเสธ
Miles Sabin


2
ฉันไม่เห็นสิ่งนี้เป็นหลัก มันระเหยหรือจบลงในสาขาหลัง 2.10.0 หรือ ... ?
Rex Kerr

9

ด้วยเหตุนี้การร่ายเวทย์มนต์จึงเปลี่ยนไปเล็กน้อยจากที่เคยเป็นเมื่อ Miles ให้คำตอบที่ยอดเยี่ยมของเขา

ต่อไปนี้ใช้งานได้ แต่เป็นที่ยอมรับหรือไม่? ฉันหวังว่าหนึ่งในศีลจะแก้ไขได้ (หรือมากกว่าปืนใหญ่หนึ่งในปืนใหญ่) หากมุมมองที่ถูกผูกไว้เป็นขอบเขตบนคุณจะสูญเสียแอปพลิเคชันไปยัง Array และ String ดูเหมือนจะไม่สำคัญว่าขอบเขตจะเป็น 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)

มีมากกว่าหนึ่งวิธีในการถลกหนังแมวที่มีเก้าชีวิต เวอร์ชันนี้บอกว่าเมื่อแหล่งที่มาของฉันถูกแปลงเป็น GenTraversableLike ตราบใดที่ฉันสามารถสร้างผลลัพธ์จาก GenTraversable ได้ก็ทำเช่นนั้น ฉันไม่สนใจ Repr เก่าของฉัน

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 เป็น 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.