前情提要:

Scala函数式编程指南(一) 函数式思想介绍

scala函数式编程(二) scala基础语法介绍

前面已经稍微介绍了scala的常用语法以及面向对象的一些简要知识,这次是补充上一章的,主要会介绍集合和函数。

注意噢,函数和方法是不一样的,方法是在类里面定义的,函数是可以单独存在的(严格来说,在scala内部,每个函数都是一个类)

一.scala集合介绍

还记得上一章介绍的object的apply方法吗,很多数据结构其实都用到了它,从而让我们可以直接用List(...)这样来新建一个List,而不用自己手动new一个。

PS:注意,scala默认的数据结构都是不可变的,就是说一个List,没法删除或增加新的元素。当然,也有“可变”的数据结构,后面会介绍到。

1.1 List

得益于apply方法,我们可以不通过new来新建数据结构。

  1. //通过工厂,新建一个List,这个List是不可变的
  2. scala> val numbers = List(1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
  3. numbers: List[Int] = List(1, 2, 3, 4, 5, 1, 2, 3, 4, 5)

1.2 元组Tuple

Tuple这个概念在python应用比较广泛,它可以将多种数据类型(Int,String,Double等)打包在一起

  1. scala> val tup = (1,1,2.1,"tuple",'c') //将多种不同数据结构打包一起,可以有重复
  2. tup: (Int, Int, Double, String, Char) = (1,1,2.1,tuple,c)

但在scala中,Tuple是有长度限制的,那就是一个Tuple最多只能有22个元素。

  1. scala> val tup = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)
  2. tup: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)
  3. //一个Tuple超过22个元素,报错了
  4. scala> val tup = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)
  5. <console>:1: error: too many elements for tuple: 23, allowed: 22
  6. val tup = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)

可以看到上面,一但一个Tuple超过22个元素,就会报错了。至于为什么是22这个神奇的数字,好像一直没有一个统一的论调。有人开玩笑的说23才对,因为有部电影的名字叫《The Number 23》~~

1.3 Map和Option

在说Map之前,需要先介绍Option,因为Map里面存的就是Option,这个后面介绍。

Option翻译过来是选项,本质上,它也确实是一个选项,用来告诉你存不存在。还是用实例说明吧:

  1. //Option里面可以存普通数据类型
  2. scala> val optionInt = Option(1)
  3. optionInt: Option[Int] = Some(1)
  4. scala> optionInt.get
  5. res8: Int = 1
  6. //但一个Option也可能为空
  7. scala> val optionNone = Option(null)
  8. optionNone: Option[Null] = None
  9. //当Option里面是空的时候,是get不出东西的,还会报错
  10. scala> optionNone.get
  11. java.util.NoSuchElementException: None.get
  12. at scala.None$.get(Option.scala:347)
  13. at scala.None$.get(Option.scala:345)
  14. ... 32 elided
  15. //这个时候可以判断它就是空的
  16. scala> optionNone.isEmpty
  17. res11: Boolean = true
  18. //但可以用getOrElse()方法,当Option里面有东西的时候,就返回那个东西,如果没有东西,就返回getOrElse()的参数的内容
  19. scala> optionNone.getOrElse("this is null") //里面没东西
  20. res12: String = this is null
  21. scala> optionInt.getOrElse("this is null") //里面有东西
  22. res15: Any = 1

再说Map,Map里面的value的类型并不是你赋的那个数据类型,而是Option。即Map(key -> Option,key1 -> Option)。

  1. scala> val map = Map("test1" -> 1,"test2" -> 2)
  2. map: scala.collection.immutable.Map[String,Int] = Map(test1 -> 1, test2 -> 2)
  3. scala> map.get("test1")
  4. res16: Option[Int] = Some(1)
  5. scala> map.get("test3")
  6. res17: Option[Int] = None
  7. scala> map.get("test3").getOrElse("this is null")
  8. res18: Any = this is null

这样的好处是什么呢?还记得在java里面,每次都得为java为空的情况做判断的痛苦吗。在scala,这些烦恼通通不存在。有了Option,妈妈再也不用担心NullPointerException啦。

1.4 常用函数组合子

匿名函数

将集合的函数组合子,那肯定得先将匿名函数。如果有用过python中的lambda表达式,那应该就很了解这种方式了。

前面说到,在scala中,函数就是对象,匿名函数也是函数。举个简单的例子:

  1. //创建一个匿名函数
  2. scala> val addOne = (x: Int) => x + 1
  3. addOne: (Int) => Int = <function1>
  4. scala> addOne(1)
  5. res4: Int = 2

注意,函数里面是可以不用return的,最后面的那个x+1就是匿名函数的返回值了。

map,reduce

因为hadoop的出现,MapReduce都被说烂了。虽然Hadoop的Mapreduce起源自函数式的map和reduce,但两者其实是不一样的。感兴趣的可以看看我之前写过的一篇:从分治算法到 Hadoop MapReduce

函数式里面的map呢,粗略的说,其实相当一个有返回值的for循环。

  1. scala> val list = List(1,2,3)
  2. list: List[Int] = List(1, 2, 3)
  3. scala> list.map(_ + 1) //这里的(_+1)其实就是一个匿名函数 //让List中每一个元素+1,并返回
  4. res29: List[Int] = List(2, 3, 4)

至于reduce呢,也是像for循环,只是不像map每次循环是当前元素,reduce除了当前元素,还有上一次循环的结果,还是看看例子吧:

  1. scala> list.reduce((i,j) => i + j) //两个两个一起循环,这里是让两个相加
  2. res28: Int = 6

比如上面的例子,有1,2,3三个数。第一次循环的两个是1,2,1+2就等于3,第二次循环就是上次的结果3和原本的3,3+3等于6,结果就是6。

filter

filter故名思意,就是过滤的意思,可以在filter中传进去一个匿名函数,返回布尔值。返回true的则保留,返回false的丢弃。

  1. scala> val numbers = List(1, 2, 3, 4)
  2. numbers: List[Int] = List(1, 2, 3, 4)
  3. //过滤出余2等于0的
  4. scala> numbers.filter((i: Int) => i % 2 == 0)
  5. res0: List[Int] = List(2, 4)

foldLeft

这个和reduce类似,也是遍历,除了当前元素,还有上一次迭代的结果。区别在于foldLeft有一个初始值。

  1. scala> val numbers = List(1, 2, 3, 4)
  2. numbers: List[Int] = List(1, 2, 3, 4)
  3. //(m: Int, n: Int) => m + n这部分是一个匿名函数
  4. scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n)
  5. res30: Int = 10

二.scala函数

在最前面有介绍到,函数就是对象。那为什么函数能直接运行呢?这其实得益于object的apply这个语法糖。

偏函数

偏函数(PartialFunction),从某种意义上来说,偏函数也是scala中挺重要的一个语法糖。

偏函数它长这样:

PartialFunction[A, B] //接收一个A类型的参数,返回B类型的参数

值得一提的是,scala中有一个关键字case,就是使用偏函数。继续举例子:

  1. //下面的case就是一个偏函数PartialFunction[Int, String]
  2. scala> val one: PartialFunction[Int, String] = { case 1 => "one" }
  3. one: PartialFunction[Int,String] = <function1>
  4. scala> one(1)
  5. res11: String = Int
  6. scala> one("one")
  7. <console>:13: error: type mismatch;
  8. found : String("one")
  9. required: Int
  10. one("one")

case关键字会匹配符合条件的类型或值,如果不符合,会报错。内部实现就不介绍了,知道是个常用语法糖就好。

而这个case关键字也正是实现scala模式匹配中,必不可少的一环,有兴趣的童鞋可以看看我这篇:scala模式匹配详细解析

这里再说一个常见应用吧,函数组合子中有一个collect,它需要的参数就是一个偏函数。下面看个有意思的例子:

  1. //这个list里面的Any类型
  2. scala> val list:List[Any] = List(1, 3, 5, "seven")
  3. list: List[Any] = List(1, 3, 5, seven)
  4. //使用map会报错,因为map接收的参数是普通函
  5. scala> list.map { case i: Int => i + 1 }
  6. scala.MatchError: seven (of class java.lang.String)
  7. at $anonfun$1.apply(<console>:13)
  8. at $anonfun$1.apply(<console>:13)
  9. at scala.collection.immutable.List.map(List.scala:277)
  10. ... 32 elided
  11. //但如果用collect函数就可以,因为collect接收的参数是偏函数,它会自动使用偏函数的一些特性,所以可以自动过滤掉不符合的数据类型
  12. scala> list.collect { case i: Int => i + 1 }
  13. res15: List[Int] = List(2, 4, 6)

因为collect接收的参数是偏函数,它会自动使用偏函数的特性,自动过滤不符合的数据类型,而map就做不到。

部分应用函数

所谓部分应用的意思,就是说,当调用一个函数时,可以仅传递一部分参数。而这样会生成一个新的函数,来个实例看看吧:

  1. //定义一个打印两个输出参数的函数
  2. scala> def partial(i:Int,j:Int) : Unit = {
  3. | println(i)
  4. | println(j)
  5. | }
  6. partial: (i: Int,j: Int)Unit
  7. //赋一个值给上面那个函数,另一个参数不赋值,生成一个新的函数
  8. scala> val partialFun = partial(5,_:Int)
  9. partialFun: Int => Unit = <function1>
  10. //只要一个参数就可以调用啦
  11. scala> partialFun(10)
  12. 5
  13. 10

部分应用函数,主要是作用是代码复用,同时也能够增加一定的代码可读性。

当然还有更多有意思的用法,后面有机会说到再说。

函数柯里化

刚开始,听到柯里化的时候很奇怪。柯里?啥玩意?

后来才知道,其实柯里是从curry音译过来的,这个是个人名,就是发明了柯里化的发明人。

柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

看看具体是怎么使用吧:

  1. //我们知道函数可以这样定义,让它接收两个参数
  2. scala> def curring(i:Int)(j: Int): Boolean = {false}
  3. curring: (i: Int)(j: Int)Boolean
  4. //可以把这个函数赋值给一个变量,注意变量的类型
  5. scala> val curringVal:(Int => (Int => Boolean)) = curring _
  6. curringVal: Int => (Int => Boolean) = <function1>
  7. //可以让这个变量接收一个参数,又变成另一个函数了
  8. scala> val curringVal_1 = curringVal(5)
  9. curringVal_1: Int => Boolean = <function1>
  10. //再用这个变量接收一个参数,终于能返回结果了
  11. scala> curringVal_1(10)
  12. res32: Boolean = false

柯里化其实是把一个函数变成一个调用链的过程,和上面的部分应用函数看起来有点像。

这几个部分初次看可能不知道它究竟有什么用,其实这些功能的一个主要用途是函数式的依赖注入。通过这部分技术可以把被依赖的函数以参数的形式传递给上层函数。限于篇幅这里就先省略,后面再介绍。

结语:

此次介绍的是scala集合的一些内容,以及一些函数的特性,再说一遍,函数其实就是对象。

我一直有一种观点,在学习新的东西的时候,一些偏固定规则的东西,比如语法。不用全部记熟,只要知道大概原理,有个映像就行。

比如说scala的函数式编程,或是java的OOP,不需要抱有先把语法学完,再学习相关的编程理念,这在我看来是有点本末倒置了。

我一般的做法,是先熟悉大概的语法,然后去学习语言的精髓。当碰到不懂的时候,再反过来查询具体的语法,有了目标之后,语法反而变得不是那么枯燥了。

以上只是我的一些个人看法,那么本篇到此就结束了。

以上~~

Scala函数式编程(三) scala集合和函数的更多相关文章

  1. 9、scala函数式编程-集合操作

    一.集合操作1 1.Scala的集合体系结构 // Scala中的集合体系主要包括:Iterable.Seq.Set.Map.其中Iterable是所有集合trait的根trai.这个结构与Java的 ...

  2. scala 函数式编程之集合操作

    Scala的集合体系结构 // Scala中的集合体系主要包括:Iterable.Seq.Set.Map.其中Iterable是所有集合trait的根trai.这个结构与Java的集合体系非常相似. ...

  3. Scala实战高手****第12课:Scala函数式编程进阶(匿名函数、高阶函数、函数类型推断、Currying)与Spark源码鉴赏

    /** * 函数式编程进阶: * 1.函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量 * 2.函数更常用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称 ...

  4. scala函数式编程(二) scala基础语法介绍

    上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些ja ...

  5. Scala函数式编程(四)函数式的数据结构 上

    这次来说说函数式的数据结构是什么样子的,本章会先用一个list来举例子说明,最后给出一个Tree数据结构的练习,放在公众号里面,练习里面给出了基本的结构,但代码是空缺的需要补上,此外还有预留的test ...

  6. Scala函数式编程(四)函数式的数据结构 下

    前情提要 Scala函数式编程指南(一) 函数式思想介绍 scala函数式编程(二) scala基础语法介绍 Scala函数式编程(三) scala集合和函数 Scala函数式编程(四)函数式的数据结 ...

  7. Scala函数式编程(五) 函数式的错误处理

    前情提要 Scala函数式编程指南(一) 函数式思想介绍 scala函数式编程(二) scala基础语法介绍 Scala函数式编程(三) scala集合和函数 Scala函数式编程(四)函数式的数据结 ...

  8. Scala函数式编程(六) 懒加载与Stream

    前情提要 Scala函数式编程指南(一) 函数式思想介绍 scala函数式编程(二) scala基础语法介绍 Scala函数式编程(三) scala集合和函数 Scala函数式编程(四)函数式的数据结 ...

  9. Scala函数式编程进阶

    package com.dtspark.scala.basics /** * 函数式编程进阶: * 1,函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量: * 2, 函数更长用的方式 ...

随机推荐

  1. 基于HttpClient4.5.1实现Http访问工具类

    本工具类基于httpclient4.5.1实现 <dependency> <groupId>org.apache.httpcomponents</groupId> ...

  2. Spring中jdbcTemplate的用法实例

    一.首先配置JdbcTemplate: 要使用Jdbctemplate 对象来完成jdbc 操作.通常情况下,有三种种方式得到JdbcTemplate 对象.       第一种方式:我们可以在自己定 ...

  3. 图解Java数据结构之队列

    本篇文章,将对队列进行一个深入的解析. 使用场景 队列在日常生活中十分常见,例如:银行排队办理业务.食堂排队打饭等等,这些都是队列的应用.那么队列有什么特点呢? 我们知道排队的原则就是先来后到,排在前 ...

  4. PySpark SQL 相关知识介绍

    title: PySpark SQL 相关知识介绍 summary: 关键词:大数据 Hadoop Hive Pig Kafka Spark PySpark SQL 集群管理器 PostgreSQL ...

  5. 百度地图获取定位,实现拖动marker定位,返回具体的位置名

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  6. 设计模式(C#)——03建造者模式

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321       当一个复杂对象由一些子对象构成,并且子对象的变化会导致复杂对象的修改.这时我们需要提供一种"封装机制&qu ...

  7. unity编辑器扩展_07(创建对话框,检测按钮的点击,点击按钮后提示信息,保存设置的数据,显示点击按钮后的处理的进度条信息)

    代码: using UnityEditor;using UnityEngine; public class ChangeValue : ScriptableWizard {               ...

  8. POJ 3186

    题意略. 思路:有一点区间dp的意思. 我令dp[ i ][ j ]表示:区间[1 , i]和区间[j , N]按某种顺序插值排好,所能获得的最大值. 状态转移方程:dp[ i ][ j ] = ma ...

  9. MSIL实用指南-比较运算

    数值的比较就是大于.小于.等于.大于等于.小于等于.不等于,它们的运算结果都是布尔值.大于.小于.等于有直接对应的指令,分别是Cgt.Clt.Ceq.大于等于.小于等于.不等于没有直接对应的指令,它的 ...

  10. 什么是Scrum?

    转自:http://www.scrumcn.com/agile/scrum-knowledge-library/scrum.html SCRUM 是一个用于开发和维护复杂产品的框架 Scrum 是一个 ...