课程包括
基本数据结构 Basic Data Structures
函数式选择器 Functional Combinators
Scala 提供一些很棒的集合量。
也可以看看 Effective Scala 里面的观点collections
scala> val numbers = List(1,2,3,4)
numbers: List[Int] = List(1,2,3,4)
集合里面没有重复项
scala> Set(1, 1, 2)
res0:scala.collection.immutable.Set[Int] = Set(1, 2)
元组不通过定义类的形式简单的把逻辑集合量组合在一起。
scala> val hostPort = ("localhost", 80)
hostPort: (String, Int) = (localhost, 80)
和case classes 不一样,它们没有命名访问函数,它们同通过基于1而不是0开始的位置命名来访问。
scala> hostPort._1
res0: String = localhost
scala> hostPort._2
res1: Int = 80
元组可以很好的适应模式匹配。
hostPort match {
case ("localhost", port) => ...
case (host, port) => ...
}
元组有一个特殊的调味剂,简单的通过两个值: -> 来定义
scala> 1 -> 2
res0: (Int, Int) = (1,2)
在Effective Scala 里面有更多关于描述绑定的(解包元组)内容。
它可以存储基本类型数据。
Map(1 -> 2)
Map("foo" -> "bar")
这个语法看起来很特殊,不过我们之前讨论元组的时候就曾经用过 -> 来创建元组。
Map() 也可以使用我们之前在第一课介绍的可变参数语法:Map(1 -> “one”, 2 -> “two”),可以展开成为Map((1, “one”),(2,”two”)),其中第一个元素是key,第二个是对应映射的值。
映射可以包含映射甚至是做为值的函数。
Map(1 -> Map("foo" -> "bar"))
Map("timesTwo" -> { timesTwo(_) })
选项是一个可能包含或可能不包含一些东西的容器。
选项的最基本的接口看起来这样:
trait Option[T] { def isDefined: Boolean def get: T def getOrElse(t: T): T }
选项自己是通用类,它可以包含两个子类:Some[T] 或者 None。
让我们来看看使用选项的例子:
Map.get 使用Option 来返回它的类型。选项告诉你方法可能并不返回你想要的东西。
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 方法可做为附加条件的。
//我们想要给一个数乘以2,否则就返回0 val result = if (res1.isDefined) { res1.get * 2 } else { 0 }
我们建议你使用getOrElse 或者模式匹配来处理结果。
getOrElse 让你方便的定义默认值。
val = result = res1.getOrElse(0) * 2
选项可以很自然的应用于模式匹配:
val result = res1 match { case Some(n) => n * 2 case None => 0 }
这里有Effective Scala 里面关于选项的观点。
List(1, 2, 3) map squared 给列表应用squared 函数,返回一个新的list,可能是List(1, 4, 9)。我们把这个操作叫做类map 选择器。(如果你想要更好的定义,你应该会喜欢Stackoverflow 上面的Explanation of combinators)。它们大多数时候应用于基本数据结构。
通过把没一个函数放到list 里面来求值的函数,返回一个含有相同元素数的list。
scala> numbers.map((i: Int) => i * 2)
res0: List[Int] = List(2, 4, 6, 8)
或者部分传入求值函数。
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 和map 类似,不过它不返回值,foreach 仅适用于它的副作用。
scala> numbers.foreach((i: Int) => i * 2)
不返回值。
你可以试着把返回的值存储起来不过它会是Unit 类型(就像,void)
scala> val doubled = numbers.foreach((i: Int) => i * 2)
doubled: Unit = ()
根据你传入函数计算为false 的元素删除。返回Boolean 的函数经常被称为谓词函数(predicate functions)。
scala> number.filter((i: Int) => i % 2 == 0)
res0: List[Int] = List(2,4)
scala> def isEven(i: Int): Boolean = i % 2 == 0
isEven: (i: Int)Boolean
scala> numbers.filter(isEven _)
res2: List[Int] = List(2, 4)
zip 把两个lists 组合成为一个list。
scala> List(1, 2, 3).zip(List("a","b","c"))
res0: List[(Int, String)] = List((1,a), (2,b), (3,c))
partition 基于一个谓词函数分割list。
scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> numbers.partitin(_ %2 == 0)
res0: (List[Int], List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9))
find 返回集合里面匹配谓词函数的第一个元素。
scala> numbers.find((i: Int) => i > 5)
res0: Option[Int] = Some(6)
drap 删除第i 个元素
scala> numbers.drop(5)
res0: List[Int] = List(6, 7, 8, 9, 10)
dropWhile 删除匹配谓词函数的第一个元素。比如,如果dropWhile 从list numbers 里面删除奇数元素,1 会被删除(而不是被2 隔离了的3)。
scala> numbers.dropWhile(_ %2 != 0)
res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> numbers.foldLeft(0)((m: Int,n: Int) => m + n)
res0: Int = 55
0 是开始值,(记住numbers 是一个List[Int]),m 是一个累加器。
这样看起来更形象:
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: 8
m: 45 n: 9
res0: Int = 55
foldRight 看起来和foldLeft 一样,只不过它是逆向运行的
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 折叠一个级别的嵌套结构。
scala> List(List(1, 2),List(3, 4)).flatten
res0: List[Int] = List(1, 2, 3, 4)
flatMap 是一个人把mapping 和flatting 联接起来的一个很常用的联接符。flatMap 接受一个嵌套函数列表然后把他们组合起来。
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)
可以考虑把它作为mapping 和flatterning 的简写:
scala> nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).faltten
res1: List[Int] = List(2, 4, 6, 8)
这个例子调用map 然后又调用flatten 做为这两个函数很自然的的一个连接。
Effective Scala 里面有关于flatMap 的信息。
现在我们学习了集合量的混合函数。
我们接下来的要做的是写出我们自己的函数式选择器。
有趣的是,下面展示的每一个函数式选择器都被写在fold 的顶部。让我们看看例子。
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 还没有聪明到明白你想要一个空的Ints 的 list然后在来累积它。
所有的函数式选择器都是用Maps 来做例子。Map 可以被当成是list 的搭档,所以你写的函数在一组键值对上起作用。
scala> val extensions = Map("steve" -> 100, "job" -> 201)
extensions: scala.collection.immutable.Map[String, Int] = Map((steve,100),(bob,101),(joe,201))
现在来以电话扩展低于200 为入口过滤。
scala> extensions.filter((namePhone: (String, Int)) => namePhone._2 < 200)
res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101))
因为它返回一个元组,你可以通过位置访问函数来取出键和值,很讨厌。
幸运的是,我么也可以用模式匹配来更好的取出键和值。
scala> extensions.filter({case (name, extension) => extension < 200})
res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101))
为什么这样也行呢?为什么可以传入部分模式匹配呢?
下节课再说~