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]].
List, bunlarlamap.