SORU
23 Mart 2011, ÇARŞAMBA


Nasıl Scala koleksiyonları zenginleştirmek-benim-kütüphane desen başvurabilirim?

En güçlü desenleri Scala mevcut bir örtülü dönüşüm için kullandığı-benim-kütüphane zenginleştirmek* desen,görünürdinamik yöntem çözünürlük gerektirmeden varolan sınıflar için eklemek için. Eğer tüm dizeleri kaç kişi vardı saydım yöntemi spaces olduğunu diledi, örneğin, olabilir:

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

Ne yazık ki, bu model genel koleksiyonları ile uğraşırken sıkıntı içine çalışır. Örneğin, bir dizi soru grouping items sequentially with collections hakkında istendi. Hiç bir şekilde inşa edilmiş bu eserler tek çekim, bu yüzden bu gibi ideal bir aday için zenginleştirmek-benim-kütüphane desen kullanarak bir genel toplama C ve bir genel öğe türü 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
    }
  }
}

hariç, tabiiişe yaramıyor. ÇOĞALTMA söyler:

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

Orada iki sorun: nasıl C[A] boş bir liste (veya ince hava) C[C[A]] muyuz? Ve nasıl Seq[Seq[A]] yerine C[C[A]] same : geri bir hat almak mı?

*Eskiden pimp-my-kütüphane olarak da bilinir.

CEVAP
23 Mart 2011, ÇARŞAMBA


Bu sorunu anlamanın anahtarı olduğunu fark etmektiriki farklı şekilde ve koleksiyonları ile inşa çalışmasıkoleksiyonlar bu kütüphanede. Tüm güzel kendi yöntemleri ile ortak koleksiyonlar arayüzü. Yaygın olarak kullanılan diğeroluşturmahemen hemen hiç bunun dışında kullanılan koleksiyonlar kütüphane, ama, inşaatçılar.

Zenginleştirici sorunumuz Bu koleksiyon aynı türden koleksiyon dönmek için çalışırken kendisi yüzleri kütüphane, bu tam olarak aynıdır. , Koleksiyon inşa etmek istiyoruz, ama genel olarak çalışırken, başvurmak için bir yol yok "koleksiyonu zaten aynı tip". İhtiyacımız varinşaatçılar.

Şimdi soru şu: nereden bizim inşaatçılar alacağız? Belli bir yerde toplanması kendisinden.Bu işe yaramıyor. Biz zaten toplama türünü unutmak için gittiğini, genel toplama taşıma, karar verdi. Yani toplama istediğimiz türü daha koleksiyonları oluşturmak oluşturucu dönüş mümkün olmasa da, türünün ne olduğunu Bilmiyormuş.

Bunun yerine, etrafında yüzen CanBuildFrom implicits bizim ustalar. Bu özellikle giriş ve çıkış türleri eşleşen ve uygun şekilde yazılmış bir inşaatçı onlara hizmet için var.

Bu yüzden, yapmak için iki kavramsal sıçramaları var:

  1. Standart koleksiyonları işlemleri kullanmıyoruz, inşaatçılar kullanıyoruz.
  2. Bizim koleksiyonundan CanBuildFromörtülü s, Bu inşaatçılar doğrudan olsun.

Bir örneğe bakalım.

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

Bu ayrı ele alalım. İlk olarak, sipariş için inşa koleksiyonu-koleksiyonlar biliyoruz gerek inşa iki tür koleksiyonlar: C[A] Her bir grup için, ve C[C[A]] Bu toplar tüm grupları bir araya. Böylece, iki inşaatçılar, As alır ve C[A]s oluşturur, ve C[A]s alır bir tane lazım ve oluşturur C[C[A]]ler. CanBuildFrom türü imzası bakarak görüyoruz

CanBuildFrom[-From, -Elem,  To]

bu da demektir ki CanBuildFrom bilmek istiyor bu tür toplama başlıyoruz ile bizim davamız, C[A] ve sonra öğeleri oluşturulan Koleksiyonu ve türü, toplama. Bu örtük parametreleri cbfcc cbfc gibi dolduruyoruz.

Bu gerçekleştiğinde, bu işin çoğunu. CanBuildFroms bize inşaatçılar (tüm yapmanız gereken bunları uygulamak) vermek için kullanabiliriz. Ve bir oluşturucu oluşturmak koleksiyonu ile =, dönüştürmek için koleksiyonu için gerekiyor sonuçta result ve boş kendisi ve başlatmak üzere hazır yine clear. İnşaatçılar bizim ilk derleme hatası çözer boş başlamak, ve özyineleme yerine inşaatçılar kullanarak olduğumuza göre, ikinci hata da gider.

Son bir küçük ayrıntı aslında iş yapar, bu algoritma dışında bir örtülü dönüşüm. new GroupingCollection[A,C] [A,C[A]] değil unutmayın. Bu sınıf bildirimi A geçen ile kendini dolduran bir parametre ile C olmasıdır. Biz sadece 43 ** ve bunun dışında C[A] yaratalım tip el. Küçük bir detay, ama eğer başka bir yol denerseniz derleme zamanı hataları alırsınız.

Burada, bu yöntem biraz "element" koleksiyonu; bunun yerine, bu yöntem, sıralı ögelerinin test başarısız olduğunda ayrı özgün koleksiyon keser. eşit veya daha fazla jenerik yaptım

Hadi eylem bizim yöntemi

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

Çalışıyor!

Tek sorun, genel olarak üst üste iki örtülü dönüşüm gerektirir beri bu yöntemler dizi var, bilmiyoruz. Diziler için ayrı bir örtük dönüştürme yazma da dahil olmak üzere bu hareket, WrappedArray ve döküm için çeşitli yollar vardır.


Edit: kodu da diziler ve dizeleri ve bu ile başa çıkmak için tercih yaklaşımımdaha fazlagenel ve uygun bir örtülü dönüşüm onlara daha spesifik tekrar diziler de çalışacak şekilde yapmak için kullanın. Bu durumda:

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

Burada yaptığımız ilave bir örtülü o verir bize bir Iterable[A] C--en koleksiyonları bu sadece kimliğini (örneğin List[A] zaten Iterable[A]), ama diziler olacak gerçek bir örtük dönüştürme. Ve, sonuç olarak, yaptığımız düştü gereksinimi C[A] <: Iterable[A]--temelde sadece gereksinim için <% açık, biz de kullanabilir açıkça iradesini yerine derleyici yazalım. Ayrıca, bunun yerine bizim C[C[A]]----koleksiyonlar koleksiyon kısıtlama rahat ettik, daha sonra olacak şeyler için doldurun biz D[C], herhangi bir. Bu daha sonra doldurmak için gidiyoruz çünkü, yöntem düzeyi yerine sınıf seviyesine kadar zorladık. Aksi halde, temelde aynı.

Şimdi soru bu nasıl kullanılacağı. Düzenli koleksiyonları için:

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

şimdi C C[A] D[C] C[C[A]] fiş. Ne karşılık gelen düz tutun, böylece new GroupingCollection çağrısı üzerine açık genel türleri ihtiyacımız olduğunu unutmayın. Bu otomatik olarak diziler kolları implicit c2i: C[A] => Iterable[A] için teşekkürler.

Ama bekleyin, eğer dizeleri kullanmak istersek ne olacak? Bir çünkü şimdi başımız dertte, "dize dize". Bu ekstra soyutlama yardımcı olur: D uygun dizeleri tutmak için bir şey diyebiliriz. Hadi seç 64**, aşağıdakileri yapın:

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

İhtiyacımız olan yeni bir CanBuildFrom idare binası, vektör dizeleri (ama bu gerçekten çok kolay, çünkü biz, biz sadece ihtiyaç için Ara Vector.newBuilder[String]), ve sonra biz doldurmak için gereken her türlü böylece GroupingCollection yazılan makul. Biz zaten dizeleri karakter koleksiyonları yapılabilir [String,Char,String] bir CanBuildFrom etrafında yüzen, unutmayın.

Bunu bir deneyelim:

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

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • BSA

    BSA

    9 NİSAN 2012
  • Damien Walters

    Damien Walte

    20 AĞUSTOS 2006
  • MrDevin521

    MrDevin521

    18 Temmuz 2010