Archive

Archive for April 19th, 2010

Scala 集合框架:Tuple,Map[K,V],Option[T]

April 19th, 2010 leeing No comments

Tuple

Tuple 能让一个方法返回两个或三个值,例如下面的代码输入一个 List[Double],返回元素个数,元素的和及其平方和:

def sumSq(in: List[Double]): (Int, Double, Double) =
in.foldLeft((0, 0d, 0d))((t, v) => (t._1 + 1, t._2 + v, t._3 + v * v))

这里,编译器会将在括号中的集合元素看作是一个Tuple,上面的代码会翻译成:Tuple3[Int,Double,Double],这个函数接收两个参数:t和 v,t是一个Tuple3,而v是一个Double。
使用模式匹配可以让上面的代码更易理解:

def sumSq(in: List[Double]) : (Int, Double, Double) =
in.foldLeft((0, 0d, 0d)){
case ((cnt, sum, sq), v) => (cnt + 1, sum + v, sq + v * v)}

当然,也可以用其它方式来创建一个Tuple:

scala> Tuple2(1,2) == Pair(1,2)
scala> Pair(1,2) == (1,2)
scala> (1,2) == 1 -> 2

其中最后一种写法 1 -> 2 对于传递pairs 是很有用的一种方式。Pairs经常出现在代码中,包括用来创建 Maps 的 name/value 对。

对于Tuple元素的读取,应该使用下标 _n,例如 t._1, t._2 ,t._3 ,注意这里起始为1,并且对元素的引用不能越界。

Map[K,V]

Map 是一个 key/value 对的集合, key 是唯一的,但value 可以不唯一,Scala中的默认Map实现是不可变的,可以用于多线程环境,而且性能与Java中的HashMap相当。
创建一个 Map:

scala> var p = Map(1 -> "David", 9 -> "Elwood")
p: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,David), (9,Elwood))

添加一个元素:

scala> p += 8 -> "Archer"

scala>  p = p + (9 -> "Archer")
p: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> David, 8 -> Archer, 9 -> Archer)

如果想取出一个不存在的元素:

scala> p(88)
java.util.NoSuchElementException: key not found: 88

这里会抛出异常,虽然看起来并不方便,因为Java的Map能够通过返回null来处理这个情况,但这样做有两个缺点:首先,必须对每个Map的访问进行 null-test,其次,这代表着不能在一个Map中存储null值。对此Scala有一个更好的机制,即Map的get()方法将返回一个包含了結果的Option(Some 或none):

scala> p.get(88)
res10: Option[java.lang.String] = None

如果key没有找到,可以返回一个默认值:

scala> p.getOrElse(99, "Nobody")
res55: java.lang.String = Nobody
scala> p.getOrElse(1, "Nobody")
res56: java.lang.String = David

也可以使用flatMap来返回key在1-5之间的值:

scala> 1 to 5 flatMap(p.get)
res13: scala.collection.immutable.IndexedSeq[java.lang.String] = Vector(David)

注意,表面上来看这里p.get 只是一个方法,但是Scala正期待着一个拥有特定类型的参数,而你传递了一个接收一个参数的方法,这时Scala会将这个方法和它未写的参数提升为一个函数。

Read more…

Categories: Scala Tags:

Scala 集合框架:List

April 19th, 2010 leeing No comments

本文的示例代码来自 <Beginning Scala> ,由于Scala中的集合框架已经重写, 这里的代码输出都是在 Scala 2.8.0RC1 下运行得出的,跟原书中有一定的差距。

1. Range

collections 可以拥有任意数量的元素,或者局限于零个或一个元素(如 Option ),Collections 可以是strict 或是 lazy 的。Lazy Collection 可以拥有不消耗内存的元素,直到它们被访问。例如一个 Range 类:

scala> 0 until 10 by 3
res7: scala.collection.immutable.Range = Range(0, 3, 6, 9)

在这里,Range 中的元素在被访问之前并不会被实例化,下面代码只访问了前5个元素:

scala> (1 to Integer.MAX_VALUE - 1).take(5)
res8: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5)

这个例子中不会消耗很多内存,因为只有需要的元素才被创建。

2. 构造一个列表

Collection 可能是可变的(即引用的内容可以被改变),也可能是不可变的(即一个引用所指向的东西不可以被改变),但注意的是不可变的Collections 可以包含可变的项目。

Scala 的list[T]是一个linked list 类型的T,代表它可以是任意类型的顺序列表。在内部,List 是由 :: 构造的, 即Scala 的 :: 类,例如:

scala> 1::2::3::Nil
res9: List[Int] = List(1, 2, 3)

实际上,它和下面的代码是相同的:

scala> new ::(1, new ::(2, new ::(3, Nil)))
res10: scala.collection.immutable.::[Int] = List(1, 2, 3)

3. 添加元素

:: 接收 head 为单个元素,而tail 则是另外一个 list , 其中 :: 左边的是 head,而 :: 右边则是tail。

也可以利用 apply 方法生成一个list :

def apply[T](param: T*)

添加一个元素:

scala> val x = List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)
scala> 99::x
res12: List[Int] = List(99, 1, 2, 3, 4)

注意在这里 x 并没有改变,而是创建了一个有新的head 和老的tail 的list。这个过程是很快的,是一个复杂度为O(1)的操作。

4. 归并列表

可以归并两个列表从而生成一个新的List,这个操作也是O(n),其中 n 是第一个List中的元素的个数。

scala> val x = List(1,2,3)
scala> val y = List(99, 98, 97)
scala> x ::: y
res3: List[Int] = List(1, 2, 3, 99, 98, 97)

过滤filter:

scala> List(1,2,3).filter(x => x % 2 == 1)

Read more…

Categories: Scala Tags: