from : http://www.importnew.com/3673.html
这个章节的内容包含
- 基本数据结构
- List
- Set
- Tuple
- Maps
- 函数组合器
- map
- foreach
- filter
- zip
- partition
- find
- drop and dropWhile
- foldRight and foldLeft
- flatten
- flatMap
- 广义的函数组合器
- 如何处理好Map?
基本数据结构
Scala提供了一些很方便的集合类。
参考 《Effective Scala》中关于怎么使用集合类的内容。
List
1
2
|
scala> val numbers = List(
1
,
2
,
3
,
4
)
numbers: List[Int] = List(
1
,
2
,
3
,
4
)
|
Set
集合中没有重复元素
1
2
|
scala> Set(
1
,
1
,
2
)
res0: scala.collection.immutable.Set[Int] = Set(
1
,
2
)
|
元组(Tuple)
元组可以直接把一些具有简单逻辑关系的一组数据组合在一起,并且不需要额外的类。
1
2
|
scala> val hostPort = (
"localhost"
,
80
)
hostPort: (String, Int) = (localhost,
80
)
|
和case class不同,元组的元素不能通过名称进行访问,不过它们可以通过基于它们位置的名称进行访问,这个位置是从1开始而非从0开始。
1
2
3
4
5
|
scala> hostPort._1
res0: String = localhost
scala> hostPort._2
res1: Int =
80
|
元组可以很好地和模式匹配配合使用。
1
2
3
4
|
hostPort match {
case
(
"localhost"
, port) => ...
case
(host, port) => ...
}
|
创建一个包含2个值的元组有一个很简单的方式:->
1
2
|
scala>
1
->
2
res0: (Int, Int) = (
1
,
2
)
|
参考 《Effective Scala》中关于解除绑定(拆封一个元组)的观点。
Map
Map里可以存放基本的数据类型。
1
2
|
Map(
1
->
2
)
Map(
"foo"
->
"bar"
)
|
这个看起来是一个特殊的语法,不过回想一下前面我们讨论元组的时候,->
符号是可以用来创建元组的。
Map()可以使用我们在第一节里讲到的可变参数的语法:Map( 1 -> "one", 2 -> "two")
,它会被扩展为Map((1,"one"),(2,"two"))
,其中第一个元素参数是key,第二个元素是value。
Map里也可以包含Map,甚至也可以把函数当作值存在Map里。
1
|
Map(
1
-> Map(
"foo"
->
"bar"
))
|
1
|
Map(
"timesTwo"
-> { timesTwo(_) })
|
Option
Option
是一个包含或者不包含某些事物的容器。
Option的基本接口类似于:
1
2
3
4
5
|
trait Option[T] {
def isDefined: Boolean
def get: T
def getOrElse(t: T): T
}
|
Option本身是泛型的,它有两个子类:Some[T]
和None
我们来看一个Option的示例: Map.get
使用Option
来作为它的返回类型。Option的作用是告诉你这个方法可能不会返回你请求的值。
1
2
3
4
5
6
7
8
|
scala> val numbers = Map(
1
->
"one"
,
2
->
"two"
)
numbers: scala.collection.immutable.Map[Int,String] = Map((
1
,one), (
2
,two))
scala> numbers.get(
2
)
res0: Option[java.lang.String] = Some(two)
scala> numbers.get(
3
)
res1: Option[java.lang.String] = None
|
现在,我们要的数据存在于这个Option
里。那么我们该怎么处理它呢?
一个比较直观的方法就是根据isDefined
方法的返回结果作出不同的处理。
1
2
3
4
5
6
7
|
//如果这个值存在的话,那么我们把它乘以2,否则返回0。
val result =
if
(res1.isDefined) {
res1.get *
2
}
else
{
0
}
|
不过,我们更加建议你使用getOrElse
或者模式匹配来处理这个结构。
getOrElse让你可以很方便地定义一个默认值。
1
|
val result = res1.getOrElse(
0
) *
2
|
模式匹配可以很好地和Option
进行配合使用。
val result = res1 match { case Some(n) => n * 2 case None => 0 }
参考 《Effective Scala》中关于 Options的内容。
函数组合器
List(1,2,3) map squared
会在列表的每个元素上分别应用squared
函数,并且返回一个新的列表,可能是List(1,4,9)
。我们把类似于map
这样的操作称为组合器。(如果你需要一个更好的定义,你或许会喜欢Stackoverflow上的关于组合器的解释。
map
在列表中的每个元素上计算一个函数,并且返回一个包含相同数目元素的列表。
1
2
|
scala> numbers.map((i: Int) => i *
2
)
res0: List[Int] = List(
2
,
4
,
6
,
8
)
|
或者传入一个部分计算的函数
1
2
3
4
5
|
scala> def timesTwo(i: Int): Int = i *
2
timesTwo: (i: Int)Int
scala> numbers.map(timesTwo _)
res0: List[Int] = List(
2
,
4
,
6
,
8
)
|
foreach
foreach和map相似,只不过它没有返回值,foreach只要是为了对参数进行作用。
1
|
scala> numbers.foreach((i: Int) => i *
2
)
|
没有返回值。
你可以尝试把返回值放在一个变量里,不过它的类型应该是Unit(或者是void)
1
2
|
scala> val doubled = numbers.foreach((i: Int) => i *
2
)
doubled: Unit = ()
|
filter
移除任何使得传入的函数返回false的元素。返回Boolean类型的函数一般都称为断言函数。
1
2
|
scala> numbers.filter((i: Int) => i %
2
==
0
)
res0: List[Int] = List(
2
,
4
)
|
1
2
3
4
5
|
scala> def isEven(i: Int): Boolean = i %
2
==
0
isEven: (i: Int)Boolean
scala> numbers.filter(isEven _)
res2: List[Int] = List(
2
,
4
)
|
zip
zip把两个列表的元素合成一个由元素对组成的列表里。
1
2
|
scala> List(
1
,
2
,
3
).zip(List(
"a"
,
"b"
,
"c"
))
res0: List[(Int, String)] = List((
1
,a), (
2
,b), (
3
,c))
|
partition
partition
根据断言函数的返回值对列表进行拆分。
1
2
3
|
scala> val numbers = List(
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
)
scala> numbers.partition(_ %
2
==
0
)
res0: (List[Int], List[Int]) = (List(
2
,
4
,
6
,
8
,
10
),List(
1
,
3
,
5
,
7
,
9
))
|
find
find返回集合里第一个匹配断言函数的元素
1
2
|
scala> numbers.find((i: Int) => i >
5
)
res0: Option[Int] = Some(
6
)
|
drop & dropWhile
drop
丢弃前i个元素
1
2
|
scala> numbers.drop(
5
)
res0: List[Int] = List(
6
,
7
,
8
,
9
,
10
)
|
dropWhile
移除前几个匹配断言函数的元素。例如,如果我们从numbers列表里dropWhile
奇数的话,1
会被移除(3
则不会,因为它被2
所“保护”)。
1
2
|
scala> numbers.dropWhile(_ %
2
!=
0
)
res0: List[Int] = List(
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
)
|
foldLeft
1
2
|
scala> numbers.foldLeft(
0
)((m: Int, n: Int) => m + n)
res0: Int =
55
|
0是起始值(注意numbers是一个List[Int]),m是累加值。
更加直观的来看:
1
2
3
4
5
6
7
8
9
10
11
12
|
scala> numbers.foldLeft(
0
) { (m: Int, n: Int) => println(
"m: "
+ m +
" n: "
+ n); m + n }
m:
0
n:
1
m:
1
n:
2
m:
3
n:
3
m:
6
n:
4
m:
10
n:
5
m:
15
n:
6
m:
21
n:
7
m:
28
n:
8
m:
36
n:
9
m:
45
n:
10
res0: Int =
55
|
foldRight
这个和foldLeft相似,只不过是方向相反。
1
2
3
4
5
6
7
8
9
10
11
12
|
scala> numbers.foldRight(
0
) { (m: Int, n: Int) => println(
"m: "
+ m +
" n: "
+ n); m + n }
m:
10
n:
0
m:
9
n:
10
m:
8
n:
19
m:
7
n:
27
m:
6
n:
34
m:
5
n:
40
m:
4
n:
45
m:
3
n:
49
m:
2
n:
52
m:
1
n:
54
res0: Int =
55
|
flatten
flatten可以把嵌套的结构展开。
1
2
|
scala> List(List(
1
,
2
), List(
3
,
4
)).flatten
res0: List[Int] = List(
1
,
2
,
3
,
4
)
|
flaoMap
flatMap是一个常用的combinator,它结合了map和flatten的功能。flatMap接收一个可以处理嵌套列表的函数,然后把返回结果连接起来。
1
2
3
4
5
|
scala> val nestedNumbers = List(List(
1
,
2
), List(
3
,
4
))
nestedNumbers: List[List[Int]] = List(List(
1
,
2
), List(
3
,
4
))
scala> nestedNumbers.flatMap(x => x.map(_ *
2
))
res0: List[Int] = List(
2
,
4
,
6
,
8
)
|
可以把它当作map和flatten两者的缩写:
1
2
|
scala> nestedNumbers.map((x: List[Int]) => x.map(_ *
2
)).flatten
res1: List[Int] = List(
2
,
4
,
6
,
8
)
|
这个调用map和flatten的示例是这些函数的类“组合器”特点的展示。
See Also Effective Scala has opinions about flatMap.
参考 《Effective Scala》中关于flatMap的内容.
广义的函数组合器
现在,我们学习了一大堆处理集合的函数。
不过,我们更加感兴趣的是怎么写我们自己的函数组合器。
有趣的是,上面展示的每个函数组合器都是可以通过fold来实现的。我们来看一些示例。
1
2
3
4
5
6
7
8
|
def ourMap(numbers: List[Int], fn: Int => Int): List[Int] = {
numbers.foldRight(List[Int]()) { (x: Int, xs: List[Int]) =>
fn(x) :: xs
}
}
scala> ourMap(numbers, timesTwo(_))
res0: List[Int] = List(
2
,
4
,
6
,
8
,
10
,
12
,
14
,
16
,
18
,
20
)
|
为什么要List[Int]?因为Scala还不能聪明到知道你需要在一个空的Int列表上来进行累加。
如何处理好Map?
我们上面所展示的所有函数组合器都能都Map进行处理。Map可以当作是由键值对组成的列表,这样你写的函数就可以对Map里的key和value进行处理。
1
2
|
scala> val extensions = Map(
"steve"
->
100
,
"bob"
->
101
,
"joe"
->
201
)
extensions: scala.collection.immutable.Map[String,Int] = Map((steve,
100
), (bob,
101
), (joe,
201
))
|
现在过滤出所有分机号码小于200的元素。
1
2
|
scala> extensions.filter((namePhone: (String, Int)) => namePhone._2 <
200
)
res0: scala.collection.immutable.Map[String,Int] = Map((steve,
100
), (bob,
101
))
|
因为你拿到的是一个元组,所以你不得不通过它们的位置来取得对应的key和value,太恶心了!
幸运的是,我们实际上可以用一个模式匹配来优雅地获取key和value。
1
2
|
scala> extensions.filter({
case
(name, extension) => extension <
200
})
res0: scala.collection.immutable.Map[String,Int] = Map((steve,
100
), (bob,
101
))
|