函数内部对外部的全局状态没有任何影响,即函数的调用结果只与输入值有关,不影响全局数据
在纯函数编程中,变量是不可变的,即函数的使用方式和其他数据类型的使用方式完全一致,可以将函数赋值给变量
1、函数的定义与使用
函数也应该有类型和值的区分(就像int i = 0有类型int和值0)
类型需要明确函数接收多少参数、每个参数的类型以及返回的值
值就是函数的具体实现
val counter: (Int) => Int = { value => value + 1 }
- (Int) => Int就是类型
- { value => value + 1 }就是值, => 前是参数名,后是具体的运算语句或表达式
- 也可以简写成
val counter = (value:Int)=> value + 1
因为类型是可以根据运算推断的 - 几个简写的例子
val counter = (_:Int) + 1 //有类型时括号不能省略,等效于“x:Int=>x+1”
val add = (_:Int) + (_:Int) //等效于“(a:Int,b:Int)=>a+b”
val m2=m1.map(_*2)//map接受一个函数作为参数,相当于“m1.map(x=>x*2)”,参数的类型可以根据m1的元素类型推断出
2、高阶函数
函数可以作为其他函数的参数或返回值。
当一个函数包含其他函数作为其参数或者返回结果为一个函数时,该函数被称为高阶函数
def sum(f: Int => Int, a: Int, b: Int):Int = {
if(a > b) 0 else f(a) + sum(f, a+1, b)
}
sum(x=>x,1,5)
sum(x=>x*x,1,5)
3、闭包
当函数的执行依赖于申明在函数外部的一个或多个变量时,称这个函数为闭包
var more = 10
val addMore =(x:Int)=> x + more
var sum=0
val accumulator = (x:Int)=>sum+=x
4、偏应用函数
- 有时候函数参数可能始终取相同的值,为了避免每次都提供这些相同的值
val a=sum(1,_:Int,_:Int) //只保留了sum的后两个参数
5、针对容器的操作
遍历操作
- 标准的遍历方法为foreach
- 接收一个函数操作,类型为Elem,返回结果为U
def foreach[U](f: Elem => U) :Unit
scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> val f=(i:Int)=>println(i)
f: Int => Unit = <function1>
scala> list.foreach(f)
scala> val university = Map("XMU" ->"Xiamen University", "THU" ->"Tsinghua University","PKU"->"Peking University")
university: scala.collection.mutable.Map[String,String] = ...
scala> university foreach{kv => println(kv._1+":"+kv._2)} // 二元组的元素可以_1, _2这样调用
映射操作map和flatMap
- 最常见的操作是map和flatMap
- map方法是将函数应用到集合中的每个元素,映射得到一个新的元素,map映射的结果会返回一个与原容器类型大小相同的新容器,只不过元素的类型可能不同
scala> val books =List("Hadoop","Hive","HDFS")
books: List[String] = List(Hadoop, Hive, HDFS)
scala> books.map(s => s.toUpperCase)
//toUpperCase方法将一个字符串中的每个字母都变成大写字母
res56: List[String] = List(HADOOP, HIVE, HDFS)
scala> books.map(s => s.length) //将字符串映射到它的长度
res57: List[Int] = List(6, 4, 4) //新列表的元素类型为Int
- flatMap将某个函数应用到每个元素时,会先拍扁合并,然后再进行操作
scala> books flatMap (s => s.toList)
res58: List[Char] = List(H, a, d, o, o, p, H, i, v, e, H, D, F, S)
过滤操作
- filter 接受一个返回布尔值的函数f作为参数,并将f作用到每个元素上,将f返回真值的元素组成一个新的容器返回
val l=List(1,2,3,4,5,6) filter {x => x%2==0}
规约操作
- 对容器元素进行两两运算,将其规约为一个值
reduce
- reduce接受一个二元函数f作为参数,首先将f作用在某两个元素上并返回一个值,然后再将f作用在上一个返回值和容器的下一个元素上,在返回一个值
scala> val s1=Set(1,2,3)
s1: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> val s2 = util.Random.shuffle(s1) //打乱集合的顺序生成一个新集合
s2: scala.collection.immutable.Set[Int] = Set(3, 2, 1)
scala> s1==s2 //s1和s2只是元素顺序不一样,但从集合的角度是完全相等的
res18: Boolean = true
scala> s1.reduce(_+_) //加法操作满足结合律和交换率,所以结果与遍历顺序无关
fold
- fold方法,可以传一个初始值,然后再接受一个与reduce中一样的二元函数参数
scala> val list =List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.fold(10)(_*_) // 10*1*2*3*4*5
res32: Int = 1200
拆分操作
partition
- partition方法接收一个布尔函数,用该函数对容器元素进行遍历,以二元组的形式返回满足条件和不满足条件的两个C[T]类型的集合
scala> val xs = List(1,2,3,4,5)
xs: List[Int] = List(1, 2, 3, 4, 5)
scala> val part = xs.partition(_<3)
part: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))
groupBy
- groupBy接受一个返回U类型的函数,用该函数对容器元素进行遍历,将返回值相同的元素作为一个子容器,并与该相同的值构成一个键值对,最后返回的是一个类型为Map<U, C[T]>的映射
scala> val gby = xs.groupBy(x=>x%3) //按被3整除的余数进行划分
gby: scala.collection.immutable.Map[Int,List[Int]] = Map(2 -> List(2, 5), 1 -> List(1, 4), 0 -> List(3))
grouped和sliding
- 接受一个整形参数n,都将容器拆分为类型相同的子容器,并返回由这些子容器构成的迭代器
- grouped从左到右划分多个大小为n的容器
- sliding使用长度为n的滑动窗口,从左到右将容器截取为多个大小为n的子容器
scala> val sl = xs.sliding(3)//滑动拆分为大小为3个子容器
sl: Iterator[List[Int]] = non-empty iterator
scala> sl.next //第一个子容器
res7: List[Int] = List(1, 2, 3)
scala> sl.next //第二个子容器
res8: List[Int] = List(2, 3, 4)
网友评论