2011-11-21 78 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)我并不期待发生魔法,但我希望键入上下文会导致++选择正确的隐式工作(就像读取哈​​斯克尔中正确的类型实例一样)。 –