Scala函数式编程专题--scala集合和函数

前情提要:

Scala函数式编程专题—— 函数式思想介绍

scala函数式编程专题——scala基础语法介绍

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

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

一.scala集合介绍

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

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

1.1 List

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

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

1.2 元组Tuple

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

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

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

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)
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)

//一个Tuple超过22个元素,报错了
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)
<console>:1: error: too many elements for tuple: 23, allowed: 22
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翻译过来是选项,本质上,它也确实是一个选项,用来告诉你存不存在。还是用实例说明吧:

//Option里面可以存普通数据类型
scala> val optionInt = Option(1)
optionInt: Option[Int] = Some(1)

scala> optionInt.get
res8: Int = 1
//但一个Option也可能为空
scala> val optionNone = Option(null)
optionNone: Option[Null] = None
//当Option里面是空的时候,是get不出东西的,还会报错
scala> optionNone.get
java.util.NoSuchElementException: None.get
 at scala.None$.get(Option.scala:347)
 at scala.None$.get(Option.scala:345)
 ... 32 elided
//这个时候可以判断它就是空的
scala> optionNone.isEmpty
res11: Boolean = true
//但可以用getOrElse()方法,当Option里面有东西的时候,就返回那个东西,如果没有东西,就返回getOrElse()的参数的内容
scala> optionNone.getOrElse("this is null") //里面没东西
res12: String = this is null
scala> optionInt.getOrElse("this is null") //里面有东西
res15: Any = 1

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

scala> val map = Map("test1" -> 1,"test2" -> 2)
map: scala.collection.immutable.Map[String,Int] = Map(test1 -> 1, test2 -> 2)

scala> map.get("test1")
res16: Option[Int] = Some(1)

scala> map.get("test3")
res17: Option[Int] = None

scala> map.get("test3").getOrElse("this is null")
res18: Any = this is null

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

1.4 常用函数组合子

匿名函数

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

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

//创建一个匿名函数
scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>

scala> addOne(1)
res4: Int = 2

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

map,reduce

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

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

scala> val list = List(1,2,3)
list: List[Int] = List(1, 2, 3)

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

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

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

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

filter

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

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

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

foldLeft

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

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

//(m: Int, n: Int) => m + n这部分是一个匿名函数
scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n)
res30: Int = 10

二.scala函数

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

偏函数

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

偏函数它长这样:

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

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

//下面的case就是一个偏函数PartialFunction[Int, String]
scala> val one: PartialFunction[Int, String] = { case 1 => "one" }
one: PartialFunction[Int,String] = <function1>

scala> one(1)
res11: String = Int

scala> one("one")
<console>:13: error: type mismatch;
 found : String("one")
 required: Int
 one("one")

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

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

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

//这个list里面的Any类型
scala> val list:List[Any] = List(1, 3, 5, "seven")
list: List[Any] = List(1, 3, 5, seven)

//使用map会报错,因为map接收的参数是普通函
scala> list.map { case i: Int => i + 1 }
scala.MatchError: seven (of class java.lang.String)
 at $anonfun$1.apply(<console>:13)
 at $anonfun$1.apply(<console>:13)
 at scala.collection.immutable.List.map(List.scala:277)
 ... 32 elided

 //但如果用collect函数就可以,因为collect接收的参数是偏函数,它会自动使用偏函数的一些特性,所以可以自动过滤掉不符合的数据类型
scala> list.collect { case i: Int => i + 1 }
res15: List[Int] = List(2, 4, 6)

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

部分应用函数

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

//定义一个打印两个输出参数的函数
scala> def partial(i:Int,j:Int) : Unit = {
 | println(i)
 | println(j)
 | }
partial: (i: Int,j: Int)Unit

//赋一个值给上面那个函数,另一个参数不赋值,生成一个新的函数
scala> val partialFun = partial(5,_:Int)
partialFun: Int => Unit = <function1>

//只要一个参数就可以调用啦
scala> partialFun(10)
5
10

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

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

函数柯里化

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

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

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

看看具体是怎么使用吧:

//我们知道函数可以这样定义,让它接收两个参数
scala> def curring(i:Int)(j: Int): Boolean = {false}
curring: (i: Int)(j: Int)Boolean

//可以把这个函数赋值给一个变量,注意变量的类型
scala> val curringVal:(Int => (Int => Boolean)) = curring _
curringVal: Int => (Int => Boolean) = <function1>

//可以让这个变量接收一个参数,又变成另一个函数了
scala> val curringVal_1 = curringVal(5)
curringVal_1: Int => Boolean = <function1>

//再用这个变量接收一个参数,终于能返回结果了
scala> curringVal_1(10)
res32: Boolean = false

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

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

结语:

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

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

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

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

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

到此这篇关于Scala函数式编程专题--scala集合和函数的文章就介绍到这了,更多相关scala函数式编程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Scala函数式编程专题--函数思想介绍

    为什么我们需要学习函数式编程?或者说函数式编程有什么优势?这个系列中我会用 scala 给你讲述函数式编程中的优势,以及一些函数式的哲学.不懂 scala 也没关系,scala 和 java 是类似的,在每篇的开头我也会先说明这节中用到的 scala 语法. 为什么函数式编程这几年火起来 如 Python 一样,函数式编程(FP,即Functional Programming)也是近几年才逐渐为人们所知,但它并不是一个多么新的概念.它拥有和面向对象编程(OOP)几乎等长的历史.但纵观每件事的脉络

  • Scala函数式编程专题--scala基础语法介绍

    上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些java或者python的基础,毕竟大部分人不会将scala当作第一门学习编程的语言. 不过这些语法知识记不住也没关系,本身语法这种东西就应该在使用中被记住.这里写这篇的目的也只是梳理一遍,方便大家对语法有个初步的印象,后面可以随时查询. PS:所使用的版本是scala 2.11.8,那我们开始吧 一.

  • Scala函数式编程专题--scala集合和函数

    前情提要: Scala函数式编程专题-- 函数式思想介绍 scala函数式编程专题--scala基础语法介绍 前面已经稍微介绍了scala的常用语法以及面向对象的一些简要知识,这次是补充上一章的,主要会介绍集合和函数. 注意噢,函数和方法是不一样的,方法是在类里面定义的,函数是可以单独存在的(严格来说,在scala内部,每个函数都是一个类) 一.scala集合介绍 还记得上一章介绍的object的apply方法吗,很多数据结构其实都用到了它,从而让我们可以直接用List(...)这样来新建一个L

  • 深入理解Scala函数式编程过程

    深入理解Scala函数式编程过程 我们马上开始一段变态的过程 如果要求立方和,可以这么做 35 * 35 * 35 68 * 68 * 68 没毛病,抽象一点儿,写个函数: def cube(n: Int) = n * n * n cube(35) cube(68) 省事儿了,如果求1到10的立方和,OK,写个递归 def cube(n: Int) = n * n * n def sumCube(a: Int, b: Int): Int = if (a > b) 0 else cube(a) +

  • JavaScript函数式编程(Functional Programming)高阶函数(Higher order functions)用法分析

    本文实例讲述了JavaScript函数式编程(Functional Programming)高阶函数(Higher order functions)用法.分享给大家供大家参考,具体如下: 高阶函数(higher-order functions),就是返回其它函数的函数,或者使用其它函数作为它的参数的函数. 使用函数作为参数 因为函数本身就是一个值,所以可以让函数作为参数传递给其它的函数.JavaScript 有些函数就需要用到函数类型的参数,比如 Array.map. 比如我有一组数据: con

  • 简析Python函数式编程字符串和元组及函数分类与高阶函数

    目录 函数式编程中的字符串 不变类型元组 普通元组的声明与访问 函数式的分类 any() .all() .len().sum() 对比学习 zip().reversed().enumerate() 高阶函数 函数 max 和 min() map 函数 filter 函数 sorted 函数 同一需求的不同效率问题 函数式编程中的字符串 在函数式编程中,经常用到 Python 字符串,因其是不可变数据结构. 字符串本身是一个对象,具备很多对象方法,与常识中函数的使用不太相同,例如下述代码 my_s

  • C#函数式编程中的标准高阶函数详解

    何为高阶函数 大家可能对这个名词并不熟悉,但是这个名词所表达的事物却是我们经常使用到的.只要我们的函数的参数能够接收函数,或者函数能够返回函数,当然动态生成的也包括在内.那么我们就将这类函数叫做高阶函数.但是今天我们的标题并不是高阶函数,而是标准高阶函数,既然加上了这个标准,就意味着在函数式编程中有一套标准的函数,便于我们每次调用.而今天我们将会介绍三个标准函数,分别为Map.Filter.Fold.  Map 这个函数的作用就是将列表中的每项从A类型转换到B类型,并形成一个新的类型.下面我们可

  • Java函数式编程(二):集合的使用

    第二章:集合的使用 我们经常会用到各种集合,数字的,字符串的还有对象的.它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多.在这章中,我们探索下如何使用lambda表达式来操作集合.我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集合进行合并. 遍历列表 遍历列表是最基本的一个集合操作,这么多年来,它的操作也发生了一些变化.我们使用一个遍历名字的小例子,从最古老的版本介绍到现在最优雅的版本. 用下面的代码我们很容易创建一个不可变的名字的列表: 复制代码 代码如下:

  • 用函数式编程技术编写优美的 JavaScript_ibm

    因为函数式编程采用了完全不同的组织程序的方式,所以那些习惯于采用命令式范例的程序员可能会发现函数式编程有点难学.在这篇文章中,您将了解一些关于如何采用函数式风格,用 JavaScript 编写良好的.优美的代码的示例.我将讨论: 函数式编程概念,包括匿名函数.调用函数的不同方法,以及将函数作为参数传递给其他函数的方式. 函数式概念的运用,采用的示例包括:扩展数组排序:动态 HTML 生成的优美代码:系列函数的应用. 函数式编程概念 请告诉每个人.请把这个提交给: Digg Slashdot 在那

  • 用函数式编程技术编写优美的 JavaScript

    级别: 初级 Shantanu Bhattacharya (shantanu@justawordaway.com), 首席顾问, Siemens Information Systems Limited 2006 年 7 月 20 日 函数式或声明性编程是非常强大的编程方法,正逐渐在软件行业流行起来.这篇文章将介绍一些相关的函数式编程概念,并提供有效使用这些概念的示例.作者将解释如何使用 JavaScript(TM)(JavaScript 能导入函数式编程的构造和特性)编写优美的代码. 简介 函数

随机推荐