map函數,隱式參數CanBuildFrom的細節

http://hongjiang.info/scala-canbuildfrom-detail/數組

 

在twitter上看到過的:http://www.dotkam.com/2012/05/08/scala-fun-with-canbuildfrom/
《快學Scala》這本書的p333頁有對CanBuildFrom這個隱式參數的解讀。app

1ide

2函數

3ui

4this

5spa

6.net

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

    val b = bf(repr)翻譯

    b.sizeHint(this)

    for (x <- this) b += f(x)

    b.result

}

先要弄清楚Repr這個類型參數的含義:意思是「展示類型」?
還有Builder這個trait,裏面定義了+=和result兩個方法。

//2013.2.19 補充,從下面這個例子分析

1

"abc".map(_+1)

1

$ scala -Xprint:typer -e "\"abc\".map(_+1)"

 

這句表達式被翻譯爲StringOps.map(...) ,除了顯式傳入的 Char => Int 的函數,後邊還有個隱式的參數:(scala.this.Predef.fallbackStringCanBuildFrom[Int])

看了一下 Predef裏面,沒有找到 fallbackStringCanBuildFrom ,卻是發現了
DummyImplicit 這個類,註釋裏有提到: @see scala.Array$, method `fallbackCanBuildFrom`
Array的伴生對象確實繼承了 FallbackArrayBuilding這個類,它裏面有個隱式轉換方法:

1

2

3

4

5

implicit def fallbackCanBuildFrom[T](implicit m: DummyImplicit): CanBuildFrom[Array[_], T, ArraySeq[T]] =

    new CanBuildFrom[Array[_], T, ArraySeq[T]] {

    def apply(from: Array[_]) = ArraySeq.newBuilder[T]

    def apply() = ArraySeq.newBuilder[T]

}

// 繼續,經過斷點跟蹤了一下,是在 LowPriorityImplicits 這個class裏定義了一些隱式轉換函數,跟上面的無關。

1

2

3

4

5

implicit def fallbackStringCanBuildFrom[T]: CanBuildFrom[String, T, immutable.IndexedSeq[T]] =

    new CanBuildFrom[String, T, immutable.IndexedSeq[T]] {

    def apply(from: String) = immutable.IndexedSeq.newBuilder[T]

    def apply() = immutable.IndexedSeq.newBuilder[T]

}

見下圖:

而map方法是在 TraversableLike 這個trait裏定義的:

把斷點設置在上面的 val b = bf(repr) 這一行,先看看repr這個變量在TraversableLike裏的定義,是一個函數,返回自身對象,造型爲Repr類型。

/** The collection of type $coll underlying this `TraversableLike` object.  
*  By default this is implemented as the `TraversableLike` object itself,  
*  but this can be overridden.  
*/
def repr: Repr = this.asInstanceOf[Repr]

對於String來講,這個repr就是自身,bf(repr) 調用 CanBuildFrom.apply 方法,而fallbackStringCanBuildFrom提供的builder是
Builder[A,IndexedSeq[A]] 類型,它的實現實際是 VectorBuilder,見 IndexedSeq類的伴生對象。

每一個集合都在其伴生對象裏提供了一個隱式的CanBuildFrom對象。

builder建立好了目標類型的容器後,開始迭代當前容器執行f函數轉換,並將結果填入目標容器。

for(x <- this) b+= f(x)

這個for表達式轉化爲 StringOps.foreach(f:A=>B),實際調用的是 IndexedSeqOptimized.foreach(f:A=>B)
裏的邏輯,這個 foreach(func) 是override了 IterableLike 裏的foreach方法。

VectorBuilder的result返回的是Vector類型,它也混入了IndexedSeq特質

scala> "abc".map(_+1)
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(98, 99, 100)

整個前因後果弄清楚了。另外,Vector是樹形數組,效率比較高。

相關文章
相關標籤/搜索