摘要:这个章节的内容包含 •基本数据结构 oList oSet oTuple oMaps •函数组合器 omap oforeach ofilter ozip opartition ofind odrop and dropWhile ofoldRight and foldLeft oflatten oflatMap o广义的函数组合器 o如何处理好Map?
这个章节的内容包含
基本数据结构
List
Set
Tuple
Maps
函数组合器
map
foreach
filter
zip
partition
find
drop and dropWhile
foldRight and foldLeft
flatten
flatMap
广义的函数组合器
如何处理好Map?
基本数据结构
Scala提供了一些很方便的集合类。
List
scala> val numbers = List(1, 2, 3, 4) numbers: List[Int] = List(1, 2, 3, 4)
Set
集合中没有重复元素
scala> Set(1, 1, 2) res0: scala.collection.immutable.Set[Int] = Set(1, 2)
元组(Tuple)
元组可以直接把一些具有简单逻辑关系的一组数据组合在一起,并且不需要额外的类。
scala> val hostPort = ("localhost", 80)
hostPort: (String, Int) = (localhost, 80)
和case class不同,元组的元素不能通过名称进行访问,不过它们可以通过基于它们位置的名称进行访问,这个位置是从1开始而非从0开始。
scala> hostPort._1 res0: String = localhost scala> hostPort._2 res1: Int = 80
元组可以很好地和模式匹配配合使用。
hostPort match { case ("localhost", port) => ... case (host, port) => ... }
创建一个包含2个值的元组有一个很简单的方式:->
scala> 1 -> 2 res0: (Int, Int) = (1,2)
Map
Map里可以存放基本的数据类型。
Map(1 -> 2)
Map("foo" ->"bar")
这个看起来是一个特殊的语法,不过回想一下前面我们讨论元组的时候,->符号是可以用来创建元组的。
Map()可以使用我们在第一节里讲到的可变参数的语法:Map( 1 ->"one", 2 ->"two"),它会被扩展为Map((1,"one"),(2,"two")),其中第一个元素参数是key,第二个元素是value。
Map里也可以包含Map,甚至也可以把函数当作值存在Map里。
Map(1 -> Map("foo" ->"bar"))
Map("timesTwo" -> { timesTwo(_) })
Option
Option是一个包含或者不包含某些事物的容器。
Option的基本接口类似于:
trait Option[T] { def isDefined: Boolean def get: T def getOrElse(t: T): T }
Option本身是泛型的,它有两个子类:Some[T]和None
我们来看一个Option的示例: Map.get使用Option来作为它的返回类型。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
模式匹配可以很好地和Option进行配合使用。
val result = res1 match { case Some(n) => n * 2 case None => 0 }
函数组合器
List(1,2,3) map squared会在列表的每个元素上分别应用squared函数,并且返回一个新的列表,可能是List(1,4,9)。我们把类似于map这样的操作称为组合器。(如果你需要一个更好的定义,你或许会喜欢Stackoverflow上的关于组合器的解释。
map
在列表中的每个元素上计算一个函数,并且返回一个包含相同数目元素的列表。
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
foreach和map相似,只不过它没有返回值,foreach只要是为了对参数进行作用。
scala> numbers.foreach((i: Int) => i * 2)
没有返回值。
你可以尝试把返回值放在一个变量里,不过它的类型应该是Unit(或者是void)
scala> val doubled = numbers.foreach((i: Int) => i * 2) doubled: Unit = ()
filter
移除任何使得传入的函数返回false的元素。返回Boolean类型的函数一般都称为断言函数。
scala> numbers.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
zip把两个列表的元素合成一个由元素对组成的列表里。
scala> List(1, 2, 3).zip(List("a", "b", "c")) res0: List[(Int, String)] = List((1,a), (2,b), (3,c))
partition
partition根据断言函数的返回值对列表进行拆分。
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返回集合里第一个匹配断言函数的元素
scala> numbers.find((i: Int) => i > 5) res0: Option[Int] = Some(6)
drop & dropWhile
drop丢弃前i个元素
scala> numbers.drop(5) res0: List[Int] = List(6, 7, 8, 9, 10)
dropWhile移除前几个匹配断言函数的元素。例如,如果我们从numbers列表里dropWhile奇数的话,1会被移除(3则不会,因为它被2所“保护”)。
scala> numbers.dropWhile(_ % 2 != 0) res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
foldLeft
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: 9 m: 45 n: 10 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
flatten可以把嵌套的结构展开。
scala> List(List(1, 2), List(3, 4)).flatten res0: List[Int] = List(1, 2, 3, 4)
flaoMap
flatMap是一个常用的combinator,它结合了map和flatten的功能。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)
可以把它当作map和flatten两者的缩写:
scala> nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).flatten res1: List[Int] = List(2, 4, 6, 8)
这个调用map和flatten的示例是这些函数的类“组合器”特点的展示。
广义的函数组合器
现在,我们学习了一大堆处理集合的函数。
不过,我们更加感兴趣的是怎么写我们自己的函数组合器。
有趣的是,上面展示的每个函数组合器都是可以通过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还不能聪明到知道你需要在一个空的Int列表上来进行累加。
如何处理好Map?
我们上面所展示的所有函数组合器都能都Map进行处理。Map可以当作是由键值对组成的列表,这样你写的函数就可以对Map里的key和value进行处理。
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的元素。
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。
scala> extensions.filter({case (name, extension) => extension < 200}) res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101))
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号