Scala 2.8 çıkış


225

Scala 2.8'de bir nesne var scala.collection.package.scala:

def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
    new CanBuildFrom[From, T, To] {
        def apply(from: From) = b.apply() ; def apply() = b.apply()
 }

Bunun şu sonuçları verdiği söylendi:

> import scala.collection.breakOut
> val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)

map: Map[Int,String] = Map(6 -> London, 5 -> Paris)

Burada neler oluyor? Neden olduğu breakOutçağrıldığını bağımsız değişken olarak kardeşime karşı List?


13
Önemsiz cevap varlık, o bir argüman değil List, bunlarla map.
Daniel C. Sobral

Yanıtlar:


325

Cevap şu tanımda bulunur map:

def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That 

İki parametresi olduğunu unutmayın. Birincisi sizin işleviniz, ikincisi örtüktür. Bunu örtük olarak sağlamazsanız, Scala mevcut olan en spesifik olanı seçecektir .

hakkında breakOut

Peki, amacı breakOutnedir? Soru için verilen örneği düşünün, bir dize listesi alın, her dize bir tuple dönüştürün (Int, String)ve sonra bir Mapdışarı üretmek . Bunu yapmanın en belirgin yolu, bir ara List[(Int, String)]koleksiyon oluşturur ve sonra onu dönüştürür.

Ortaya çıkan koleksiyonu üretmek için a'nın mapkullanıldığı göz önüne alındığında, aracıyı Builderatlamak Listve sonuçları doğrudan a'ya toplamak mümkün olmaz Mapmı? Açıkçası, evet, öyle. Bununla birlikte, bunu yapmak için uygun olanı geçmemiz CanBuildFromgerekiyor mapve tam olarak breakOutbunu yapıyor.

Öyleyse, tanımına bakalım breakOut:

def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
  new CanBuildFrom[From, T, To] {
    def apply(from: From) = b.apply() ; def apply() = b.apply()
  }

Not o breakOutparametreli ve bir örneğini döndürür yani CanBuildFrom. O sırada da, türleri From, Tve Tobunu biliyoruz çünkü zaten anlaşılmaktadır edilmiştir mapbekliyor CanBuildFrom[List[String], (Int, String), Map[Int, String]]. Bu nedenle:

From = List[String]
T = (Int, String)
To = Map[Int, String]

Sonuç olarak, breakOutkendisi tarafından alınan örtük olanı inceleyelim . Türlüdür CanBuildFrom[Nothing,T,To]. Tüm bu türleri zaten biliyoruz, böylece bir örtük türe ihtiyacımız olduğunu belirleyebiliriz CanBuildFrom[Nothing,(Int,String),Map[Int,String]]. Fakat böyle bir tanım var mı?

Hadi CanBuildFromtanımına bakalım :

trait CanBuildFrom[-From, -Elem, +To] 
extends AnyRef

Yani CanBuildFromilk tip parametre üzerinde kontra-türüdür. Çünkü Nothingbir alt sınıf (yani her şeyin bir alt sınıfıdır), yani herhangi bir sınıfın yerine kullanılabileceği anlamına gelir Nothing.

Böyle bir inşaatçı mevcut olduğundan, Scala bunu istenen çıktıyı üretmek için kullanabilir.

İnşaatçılar Hakkında

Scala'nın koleksiyon kütüphanesinden birçok yöntem orijinal koleksiyonu almak, bir şekilde işlemek ( mapher bir öğeyi dönüştürmek için) ve sonuçları yeni bir koleksiyonda saklamaktan ibarettir .

Kodun yeniden kullanımını en üst düzeye çıkarmak için, sonuçların bu şekilde saklanması temel olarak iki işlemi destekleyen bir oluşturucu ( scala.collection.mutable.Builder) aracılığıyla yapılır : öğeleri ekleme ve sonuçta elde edilen koleksiyonu döndürme. Elde edilen bu koleksiyonun türü, üreticinin türüne bağlı olacaktır. Böylece, bir Listinşaatçı a geri döner List, bir Mapinşaatçı a geri döner Map, vb. mapYöntemin uygulanması , sonucun türüyle ilgilenmek zorunda değildir: oluşturucu bununla ilgilenir.

Öte yandan, bunun bir mapşekilde bu kurucuyu alması gerektiği anlamına gelir . Scala 2.8 Koleksiyonlarını tasarlarken karşılaşılan sorun, mümkün olan en iyi kurucunun nasıl seçileceği idi. Örneğin, eğer yazacak Map('a' -> 1).map(_.swap)olsaydım, Map(1 -> 'a')geri dönmek isterdim . Öte yandan, a Map('a' -> 1).map(_._1)döndüremez Map( a döndürür Iterable).

BuilderBilinen ifade türlerinden mümkün olan en iyisini üretme büyüsü, bu CanBuildFromörtük yoluyla gerçekleştirilir .

hakkında CanBuildFrom

Neler olduğunu daha iyi açıklamak için, haritalanan koleksiyonun a Mapyerine bir örnek vereceğim List. Daha Listsonra geri döneceğim . Şimdilik, şu iki ifadeyi düşünün:

Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)

Birincisi a Map, ikincisi bir Iterable. Uygun bir koleksiyon döndürmenin büyüsü işidir CanBuildFrom. mapAnlamak için tekrar tanımını düşünelim .

Yöntem mapmiras alınır TraversableLike. Bu üzerinde parametre belirlenmiştir Bve Thatve markaları tip parametrelerin kullanmak Ave Reprsınıfını parametreleştirdiğinizde. Her iki tanımı birlikte görelim:

Sınıf TraversableLikeşu şekilde tanımlanır:

trait TraversableLike[+A, +Repr] 
extends HasNewBuilder[A, Repr] with AnyRef

def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That 

Nereden Ave Reprnereden geldiğini anlamak için, Mapkendisinin tanımını düşünelim :

trait Map[A, +B] 
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]

Çünkü TraversableLikeuzatmak tüm özellikleri ile devralınan Map, Ave Reprbunlardan herhangi miras edilebilir. Sonuncusu tercih edilir. Yani, değişmez tanımını Mapve onu bağlayan tüm özellikleri takip ederek TraversableLike, elimizde:

trait Map[A, +B] 
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]

trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] 
extends MapLike[A, B, This]

trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] 
extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This]

trait IterableLike[+A, +Repr] 
extends Equals with TraversableLike[A, Repr]

trait TraversableLike[+A, +Repr] 
extends HasNewBuilder[A, Repr] with AnyRef

Eğer tip parametrelerini Map[Int, String]zincirin altından geçirirseniz, geçilen tiplerin TraversableLikeve dolayısıyla kullanılan tiplerin map:

A = (Int,String)
Repr = Map[Int, String]

Örneğe dönersek, ilk harita bir tür işlev alıyor ((Int, String)) => (Int, Int)ve ikinci harita bir tür işlev alıyor ((Int, String)) => String. Gördüğümüz gibi, alınan bir demet olduğunu vurgulamak için çift parantez kullanıyorum A.

Bu bilgi ile diğer türleri ele alalım.

map Function.tupled(_ -> _.length):
B = (Int, Int)

map (_._2):
B = String

Biz ilk tarafından döndürülen tip olduğunu görebilirsiniz mapolduğunu Map[Int,Int]ve ikinci Iterable[String]. mapTanımına bakıldığında, bunların değerleri olduğunu görmek kolaydır That. Ama nereden geliyorlar?

İlgili sınıfların yardımcı nesnelerinin içine bakarsak, bunları sağlayan bazı örtülü bildirimler görürüz. Nesnede Map:

implicit def  canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]  

IterableSınıfı genişletilen nesnede Map:

implicit def  canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]  

Bu tanımlar parametreli fabrikalar sağlar CanBuildFrom.

Scala, mevcut olan en kesin örtülü yöntemi seçecektir. İlk durumda, ilk oldu CanBuildFrom. İkinci durumda, birincisi eşleşmediği için ikincisini seçti CanBuildFrom.

Soruya Geri Dön

Türlerin nasıl çıkarıldığını görmek için List's ve map' tanımının (tekrar) sorusunun kodunu görelim :

val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)

sealed abstract class List[+A] 
extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]]

trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] 
extends SeqLike[A, Repr]

trait SeqLike[+A, +Repr] 
extends IterableLike[A, Repr]

trait IterableLike[+A, +Repr] 
extends Equals with TraversableLike[A, Repr]

trait TraversableLike[+A, +Repr] 
extends HasNewBuilder[A, Repr] with AnyRef

def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That 

Türü List("London", "Paris")olan List[String]türleri, böylece Ave Reprtanımlanan TraversableLikegibidir:

A = String
Repr = List[String]

Tip (x => (x.length, x))olduğu (String) => (Int, String)türü, yani Bgeçerli:

B = (Int, String)

Son bilinmeyen tür, Thatsonucun türüdür mapve zaten buna sahibiz:

val map : Map[Int,String] =

Yani,

That = Map[Int, String]

Bu breakOut, mutlaka, türünü veya alt türünü döndürmelidir CanBuildFrom[List[String], (Int, String), Map[Int, String]].


61
Daniel, cevabınızdaki türlerden geçebilirim, ama bir kez sona erdiğimde, üst düzey bir anlayış kazanmamış gibi hissediyorum. Ne olduğunu koparma? "BreakOut" adı nereden geliyor (neyi kırıyorum)? Bu durumda bir Harita çıkarmak için neden gereklidir? Şüphesiz orada olan bazı bu sorular kısaca cevap yolu? (her ayrıntıyı kavramak için uzun tipte
yiv açma

3
@Seth Bu geçerli bir endişe, ama göreve hazır olduğumdan emin değilim. Bunun kaynağı burada bulunabilir: article.gmane.org/gmane.comp.lang.scala.internals/1812/… . Bunu düşüneceğim, ama şu anda, onu iyileştirmenin bir yolunu düşünemiyorum.
Daniel C. Sobral

2
Map [Int, String] tüm sonuç türünü belirtmekten ve bunun yerine şöyle yazabilmekten kaçınmanın bir yolu var mı: 'val map = List ("Londra", "Paris"). Map (x => (x. uzunluk, x)) (breakOut [... Harita]) '
IttayD

9
@SethTisue Bu açıklamayı okuduğumdan, oluşturucunuzun bir List [String] 'ten oluşturması gereken gereksinimi "kırmak" için breakOut gerekli olduğu görülüyor. Derleyici, sağlayamadığınız bir CanBuildFrom [Liste [Dize], (Int, Dize), Harita [Int, Dize]] istiyor. BreakOut işlevi bunu CanBuildFrom içindeki ilk tür parametresini Nothing olarak ayarlayarak hızlandırarak yapar. Şimdi sadece bir CanBuildFrom [Nothing, (Int, String), Map [Int, String]] sağlamanız gerekiyor. Map sınıfı tarafından sağlandığından bu kolaydır.
Mark

2
@ Mark BreakOut bulduğumda, adresleme gördüğüm sorun monads kendi türüne (bind / flatMap üzerinden) eşleme ısrar yolu oldu. Bu, bir monad kullanarak farklı bir monad tipine bir eşleme zincirinin "parçalanmasına" izin verir. Yine de Adriaan Moors'un (yazarın) böyle düşünüp düşünmediğine dair hiçbir fikrim yok!
Ed Staub

86

Daniel'in cevabı üzerine inşa etmek istiyorum. Çok kapsamlıydı, ancak yorumlarda belirtildiği gibi, koparmanın ne yaptığını açıklamıyor.

Re'den alınan : Açık Builders (2009-10-23) için Destek , koparma ne yapar inanıyorum:

Derleyiciye hangi Oluşturucunun dolaylı olarak seçeceği konusunda bir öneri verir (esas olarak derleyicinin hangi fabrikaya en uygun duruma uygun olduğunu düşündüğünü seçmesine izin verir.)

Örneğin, aşağıdakilere bakın:

scala> import scala.collection.generic._
import scala.collection.generic._

scala> import scala.collection._
import scala.collection._

scala> import scala.collection.mutable._
import scala.collection.mutable._

scala>

scala> def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
     |    new CanBuildFrom[From, T, To] {
     |       def apply(from: From) = b.apply() ; def apply() = b.apply()
     |    }
breakOut: [From, T, To]
     |    (implicit b: scala.collection.generic.CanBuildFrom[Nothing,T,To])
     |    java.lang.Object with
     |    scala.collection.generic.CanBuildFrom[From,T,To]

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

scala> val imp = l.map(_ + 1)(breakOut)
imp: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4)

scala> val arr: Array[Int] = l.map(_ + 1)(breakOut)
imp: Array[Int] = Array(2, 3, 4)

scala> val stream: Stream[Int] = l.map(_ + 1)(breakOut)
stream: Stream[Int] = Stream(2, ?)

scala> val seq: Seq[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.Seq[Int] = ArrayBuffer(2, 3, 4)

scala> val set: Set[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.Set[Int] = Set(2, 4, 3)

scala> val hashSet: HashSet[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.HashSet[Int] = Set(2, 4, 3)

Dönüş türünün derleyici tarafından beklenen türle en iyi eşleşecek şekilde örtük olarak seçildiğini görebilirsiniz. Alıcı değişkeni nasıl bildirdiğinize bağlı olarak, farklı sonuçlar elde edersiniz.

Aşağıdaki, bir oluşturucu belirtmek için eşdeğer bir yol olacaktır. Bu durumda, derleyici üreticinin türüne göre beklenen türü çıkaracaktır:

scala> def buildWith[From, T, To](b : Builder[T, To]) =
     |    new CanBuildFrom[From, T, To] {
     |      def apply(from: From) = b ; def apply() = b
     |    }
buildWith: [From, T, To]
     |    (b: scala.collection.mutable.Builder[T,To])
     |    java.lang.Object with
     |    scala.collection.generic.CanBuildFrom[From,T,To]

scala> val a = l.map(_ + 1)(buildWith(Array.newBuilder[Int]))
a: Array[Int] = Array(2, 3, 4)

1
Acaba neden " breakOut" olarak adlandırılıyor ? Ben convertya da buildADifferentTypeOfCollection(ama daha kısa) bir şeyi hatırlamanın daha kolay olabileceğini düşünüyorum.
KajMagnus

8

Daniel Sobral'in cevabı harika ve Scala Koleksiyonlar Mimarisi (Scala'da Programlama Bölüm 25) ile birlikte okunmalıdır .

Sadece neden çağrıldığını ayrıntılandırmak istedim breakOut:

Neden deniyor breakOut?

Çünkü bir türden diğerine ayrılmak istiyoruz :

Hangi türden hangi türden ayrılır? Bakmak Lets mapüzerine fonksiyonu Seqbir örnek olarak:

Seq.map[B, That](f: (A) -> B)(implicit bf: CanBuildFrom[Seq[A], B, That]): That

Bir diziyi doğrudan aşağıdaki gibi bir dizinin öğeleri üzerinden eşleştirerek oluşturmak istiyorsak:

val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))

Derleyici şikayet eder:

error: type mismatch;
found   : Seq[(String, Int)]
required: Map[String,Int]

Seq yalnızca başka Seq nasıl oluşturulacağını bilir Bunun da sebebi (yani örtük var CanBuildFrom[Seq[_], B, Seq[B]]oluşturucu fabrika mevcuttur, ancak orada YOK Sek gelen Haritası'na oluşturucu fabrika).

Derlemek için, bir şekilde breakOuttür gereksinimini karşılamamız ve mapişlevin kullanması için bir Harita üreten bir oluşturucu oluşturabilmemiz gerekir.

Daniel'in açıkladığı gibi, breakOut'un aşağıdaki imzası vardır:

def breakOut[From, T, To](implicit b: CanBuildFrom[Nothing, T, To]): CanBuildFrom[From, T, To] =
    // can't just return b because the argument to apply could be cast to From in b
    new CanBuildFrom[From, T, To] {
      def apply(from: From) = b.apply()
      def apply()           = b.apply()
    }

Nothingtüm sınıfların bir alt sınıfıdır, dolayısıyla herhangi bir üretici fabrika yerine kullanılabilir implicit b: CanBuildFrom[Nothing, T, To]. Örtük parametresini sağlamak için breakOut işlevini kullandıysak:

val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))(collection.breakOut)

Çünkü derlemek istiyorum breakOutgerekli tipini verebilmektedir CanBuildFrom[Seq[(String, Int)], (String, Int), Map[String, Int]]derleyici türünde bir örtük oluşturucu fabrikası bulmak mümkün iken, CanBuildFrom[Map[_, _], (A, B), Map[A, B]]yerine, CanBuildFrom[Nothing, T, To]koparma gerçek oluşturucu oluşturmak için kullanmak için.

Harita'da CanBuildFrom[Map[_, _], (A, B), Map[A, B]]tanımlanan ve yalnızca MapBuildertemeldeki bir Haritası kullanan bir haritayı başlatan not edin .

Umarım bu şeyleri temizler.


4

Ne yaptığını anlamak için basit bir örnek breakOut:

scala> import collection.breakOut
import collection.breakOut

scala> val set = Set(1, 2, 3, 4)
set: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)

scala> set.map(_ % 2)
res0: scala.collection.immutable.Set[Int] = Set(1, 0)

scala> val seq:Seq[Int] = set.map(_ % 2)(breakOut)
seq: Seq[Int] = Vector(1, 0, 1, 0) // map created a Seq[Int] instead of the default Set[Int]

Örnek için teşekkürler! Ayrıca val seq:Seq[Int] = set.map(_ % 2).toVectoriçin Setolduğu gibi size tekrarlanan değerleri vermeyecektir map.
Matthew Pickering

@MatthewPickering doğru! set.map(_ % 2)bir Set(1, 0)ilk oluşturur ve daha sonra a dönüştürülür Vector(1, 0).
fdietze
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.