2013-01-18 49 views
1

我試圖使它可選運行map操作順序或並行,例如使用下面的代碼調用上的並行集合圖:經由參考祖先型

val runParallel = true 
val theList = List(1,2,3,4,5) 
(if(runParallel) theList.par else theList) map println //Doesn't run in parallel 

什麼我注意到的是「地圖」操作並不像我預期的那樣運行。雖然沒有條件,它會:

theList.par map println //Runs in parallel as visible in the output 

的類型,我希望是兩種類型的theListtheList.par的最接近共同祖先表達(if(runParallel) theList else theList.par)的是一個可怕的類型,我不會粘貼在這裏,但它有趣的看(通過斯卡拉控制檯:)

:type (if(true) theList else theList.par) 

爲什麼沒有map上並聯並行採集工作?

更新:這在SI-4843中討論過,但從JIRA票據中不清楚爲什麼這發生在Scala 2.9.x上。

回答

3

對它發生的原因的解釋是一個很長的故事:在Scala 2.9.x(我不知道其他版本)這些收集方法(如過濾器或映射)依賴於CanBuildFrom機制。這個想法是,你有被用來創建一個建設者新集合的隱含參數:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { 
    val b = bf(repr) 
    b.sizeHint(this) 
    for (x <- this) b += f(x) 
    b.result 
    } 

由於這種機制,地圖方法在TraversableLike特質只定義和它的子類不需要重寫它。如您所見,在方法映射簽名中有許多類型參數。讓我們來看看在瑣碎的:

  • B這是新的集合
  • A的元素的類型是源集合中的元素的類型

讓我們來看看在更復雜的問題:

  • That是新類型的集合,可以是從真正水流不同nt類型。一個經典的例子是,當你使用一個toString映射例如位集合:

    scala> val a = BitSet(1,3,5) 
    a scala.collection.immutable.BitSet = BitSet(1, 3, 5) 
    scala> a.map {_.toString} 
    res2: scala.collection.immutable.Set[java.lang.String] = Set(1, 3, 5) 
    

因爲它是非法的創建BitSet[String]地圖結果將是一個Set[String] 最後Repr在當前採集的類型。當您嘗試通過函數映射集合時,編譯器將使用類型參數解析合適的CanBuildFrom。

因爲它是合理的,映射方法被覆蓋在並行收集在ParIterableLike如下所示:

def map[S, That](f: T => S)(implicit bf: CanBuildFrom[Repr, S, That]): That = bf ifParallel { pbf => 
    executeAndWaitResult(new Map[S, That](f, pbf, splitter) mapResult { _.result }) 
    } otherwise seq.map(f)(bf2seq(bf)) 

正如你可以看到,該方法具有相同的簽名,但它採用了不同的方法:它測試提供的CanBuildFrom是否是並行的,否則回落到默認實現。 因此,Scala並行集合使用特殊的CanBuildFrom(並行)爲地圖方法創建並行構建器。

但是,當你做

(if(runParallel) theList.par else theList) map println //Doesn't run in parallel 

發生什麼地圖方法大幹快上的

(if(runParallel) theList.par else theList) 

返回類型爲兩個類的第一個共同祖先的結果執行(在這種情況下,只有一定數量的特性混合在一起)。既然它是一個共同的祖先,那麼類型參數Repr將會是某種共同祖先的兩種集合表示,我們稱之爲Repr1


結論

當你調用map方法,編譯器應該找到一個操作的適合CanBuildFrom[Repr, B, That]。由於我們的Repr1不是平行集合中的一個,因此不會有任何CanBuildFrom[Repr1,B,That]能夠提供並行構建器。對於Scala集合的實現,這實際上是一個正確的行爲,如果行爲不同,那意味着並行集合的每個映射都將並行運行。

這裏的要點是,對於如何在2.9.x中設計Scala集合,沒有其他選擇。如果編譯器沒有爲並行集合提供CanBuildFrom,則映射將不會平行。

+0

感謝您的詳細解答。 – omid