2011-11-21 119 views
5

從Scala的集合的設計我的理解是這樣的:毀林Scala集合

scala> BitSet(1,2,3) map (_ + "a") 
res7: scala.collection.immutable.Set[String] = Set(1a, 2a, 3a) 

不建立一箇中間數據結構:一套新建成的位集合的遍歷使用生成器。事實上,在這種情況下,很明顯,因爲一串字符串沒有意義。

怎麼樣從列表中映射?我很確定下面建立一箇中間列表:

scala> List(1,2,3) map (_ -> "foo") toMap 
res8: scala.collection.immutable.Map[Int,java.lang.String] = 
    Map(1 -> foo, 2 -> foo, 3 -> foo) 

即列表List((1,foo), (2,foo), (3,foo))。如果沒有,那麼如何?現在,以下情況如何?

scala> Map.empty ++ (List(1,2,3) map (_ -> "foo")) 
res10: scala.collection.immutable.Map[Int,java.lang.String] = 
    Map(1 -> foo, 2 -> foo, 3 -> foo) 

這個時候,從我似乎從++類型明白:

def ++ [B >: (A, B), That] 
     (that: TraversableOnce[B]) 
     (implicit bf: CanBuildFrom[Map[A, B], B, That]): That 

認爲它可能的情況下,地圖是建立在飛,並沒有中間名單被構造。

是這樣嗎?如果是的話,這是確保森林砍伐的標準方法,還是有一個更直接的語法?

回答

14

您可以使用breakOut確保沒有創建中間集合。例如:

// creates intermediate list. 
scala> List((3, 4), (9, 11)).map(_.swap).toMap 
res542: scala.collection.immutable.Map[Int,Int] = Map(4 -> 3, 11 -> 9) 

scala> import collection.breakOut 
import collection.breakOut 

// doesn't create an intermediate list. 
scala> List((3, 4), (9, 11)).map(_.swap)(breakOut) : Map[Int, Int] 
res543: Map[Int,Int] = Map(4 -> 3, 11 -> 9) 

您可以閱讀更多關於它here

UPDATE:

如果你讀的breakOut的定義,你會發現它基本上創建預期類型的​​CanBuildFrom對象,並明確地傳遞給該方法的一種方式。 breakOut僅僅可以避免輸入以下樣板。

// Observe the error message. This will tell you the type of argument expected. 
scala> List((3, 4), (9, 11)).map(_.swap)('dummy) 
<console>:16: error: type mismatch; 
found : Symbol 
required: scala.collection.generic.CanBuildFrom[List[(Int, Int)],(Int, Int),?] 
       List((3, 4), (9, 11)).map(_.swap)('dummy) 
               ^

// Let's try passing the implicit with required type. 
// (implicitly[T] simply picks up an implicit object of type T from scope.) 
scala> List((3, 4), (9, 11)).map(_.swap)(implicitly[CanBuildFrom[List[(Int, Int)], (Int, Int), Map[Int, Int]]]) 
// Oops! It seems the implicit with required type doesn't exist. 
<console>:16: error: Cannot construct a collection of type Map[Int,Int] with elements of type (Int, Int) based on a coll 
ection of type List[(Int, Int)]. 
       List((3, 4), (9, 11)).map(_.swap)(implicitly[CanBuildFrom[List[(Int, Int)], (Int, Int), Map[Int, Int]]]) 

// Let's create an object of the required type ... 
scala> object Bob extends CanBuildFrom[List[(Int, Int)], (Int, Int), Map[Int, Int]] { 
    | def apply(from: List[(Int, Int)]) = foo.apply 
    | def apply() = foo.apply 
    | private def foo = implicitly[CanBuildFrom[Nothing, (Int, Int), Map[Int, Int]]] 
    | } 
defined module Bob 

// ... and pass it explicitly. 
scala> List((3, 4), (9, 11)).map(_.swap)(Bob) 
res12: Map[Int,Int] = Map(4 -> 3, 11 -> 9) 

// Or let's just have breakOut do all the hard work for us. 
scala> List((3, 4), (9, 11)).map(_.swap)(breakOut) : Map[Int, Int] 
res13: Map[Int,Int] = Map(4 -> 3, 11 -> 9) 
+3

哇,這真的需要542名試圖獲得這種權利;-) –

+0

謝謝,這正是我一直在尋找。什麼我不知道的是,爲什麼斯卡拉抱怨'名單((3,4),(9,11))地圖(_交換):使用類型約束我有地圖[INT,INT]'代替。明確地選擇正確的隱式(正如read所示,根據上下文在haskell中選擇正確的類型實例)。難道這只是隱含的不以這種方式工作(我會完全理解它,我知道子類型會使一切都變得困難),還是我忽略了這種特殊情況下的某些東西? –

+3

@DuncanMcGregor:我過去5天沒有關閉REPL。我在日常的開發中大量使用它。 :-) – missingfaktor

3

例1)正確的,沒有中間列表

2)是的,你會得到一個itermediate列表。

3)再次肯定的是,您會從圓括號中得到一個intermeditate列表。沒有「魔術」在繼續。如果你在括號裏有東西,它會首先被評估。

我不確定這裏的「砍伐森林」是什麼意思:根據維基百科,它意味着消除樹結構。如果您的意思是消除中間列表,則應該使用視圖。例如,見這裏:summing a transformation of a list of numbers in scala

因此,沒有中間結果,你的例子是

BitSet(1,2,3).view.map(_ + "a").toSet 

toSet是必需的,否則你有一個IterableView[String,Iterable[_]]

List(1,2,3).view.map(_ -> "foo").toMap 

Map.empty ++ (List(1,2,3).view.map(_ -> "foo")) 

還有一個force方法用於執行轉換操作,但是這似乎有一個令人討厭的習慣,給你一個更普通的類型(也許有人可以用一個理由來評論):

scala> Set(1,2,3).view.map(_ + 1).force 
res23: Iterable[Int] = Set(2, 3, 4) 
+0

砍伐意味着消除中間的樹結構。清單僅僅是一個墮落的樹,其中每個節點''::有一個元素作爲葉左子,要麼另一個''::節點或'Nil'葉爲右孩子,這樣的術語可以應用到列表以及。 – hammar

+0

謝謝。關於3)我並不期待發生魔法,但我希望鍵入上下文會導致++選擇正確的隱式工作(就像讀取哈​​斯克爾中正確的類型實例一樣)。 –