函数式编程高级
文章目录
1. 偏函数
1.1 快速入门
val list = List(1, 2, 3, 4, "hello")
//定义一个偏函数
//1. PartialFunction[Any,Int] 表示偏函数接收的参数类型是 Any,返回类型是 Int
//2. isDefinedAt(x: Any) 如果返回 true ,就会去调用 apply 构建对象实例,如果是 false,过滤
//3. apply 构造器 ,对传入的值 + 1,并返回(新的集合)
val partialFun = new PartialFunction[Any,Int] {
override def isDefinedAt(x: Any) = {
x.isInstanceOf[Int]
}
override def apply(v1: Any) = {
v1.asInstanceOf[Int] + 1
}
}
//使用偏函数
//1. 遍历 list 所有元素
//2. 然后调用 val element = if(partialFun-isDefinedAt(list 单个元素)) {partialFun-//apply(list 单个元 素) }
//3. 每得到一个 element,放入到新的集合,最后返回
val list2 = list.collect(partialFun)
println("list2" + list2) // (2,3,4,5)
1.2 小结
- 使用构建特质的实现类(使用的方式是
PartialFunction
的匿名子类) 。 PartialFunction
是个特质(看源码) 。- 构建偏函数时,参数形式
[Any, Int]
是泛型,第一个表示参数类型,第二个表示返回参数 。 - 当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行
isDefinedAt()
如果为true
, 就会执行apply
, 构建一个新的Int
对象返回 。 - 执行
isDefinedAt()
为false
就过滤掉这个元素,即不构建新的Int
对象。 map
函数不支持偏函数,因为map
底层的机制就是所有循环遍历,无法过滤处理原来集合的元素 。collect
函数支持偏函数。
1.3 偏函数的简写形式
// 方式1:简写成 case 语句
def partialFun2: PartialFunction[Any,Int] = {
case i:Int => i + 1
}
// 方式2
val list3 = list.collect{ case i:Int => i + 1 }
2. 作为参数的函数
2.1 基本介绍
-
函数作为一个变量传入到了另一个函数中,那么该作为参数的函数的类型是:
function1
即:
(参数类型) => 返回类型
。
2.2 案例
def plus(x: Int) = 3 + x
val result1 = Array(1, 2, 3, 4).map(plus(_))
println(result1.mkString(",")) // (4,5,6,7)
map(plus(_))
中的plus(_)
就是将plus
这个函数当做一个参数传给了map
,_
这里代表从集合中遍历出来的一个元素。plus(_)
这里也可以写成plus
表示对Array(1,2,3,4)
遍历,将每次遍历的元素传给plus
的x
。- 进行
3 + x
运算后,返回新的Int
,并加入到新的集合result1
中。 def map[B, That](f: A => B)
的声明中的f: A => B
表示一个函数。
3. 匿名函数
3.1 基本介绍
-
没有名字的函数就是匿名函数,可以通过函数表达式来设置匿名函数。
-
不需要写
def 函数名
。 -
不需要写返回类型,使用类型推导。
-
=
变为=>
。 -
函数体如果有多行,使用
{}
包括。
3.2 案例
val triple = (x: Double) => { 3 * x }
print(triple(3)) // 9
4. 高阶函数
4.1 基本介绍
- 能够接受函数作为参数的函数,叫做高阶函数。
4.2 基本使用
// 定义高阶函数
def test(f: Double => Double, num: Double) {
f(num)
}
// 定义普通函数
def sum(n: Double) {
n + n
}
// 使用
val res = test(sum, 3.0)
print(res) // 9.0
4.3 高阶函数可以返回函数类型
// 1. minusxy 是高阶函数,因为它返回匿名函数
// 2. 返回的匿名函数 (y: Int) => x - y
// 3. 返回的匿名函数可以使用变量接收
def minusxy(x: Int) {
(y: Int) => x - y
}
// 使用 方式一
val f = minusxy(3)
val res = f(1) // 2
// 使用 方式二
val res = minusxy(3)(2) // 2
5. 参数类型推断
5.1 基本介绍
- 参数推断省去类型信息,参数类型是可以推断出来的,如
list=(1,2,3) list.map()
,map
中函数参数类型是可以推断的,同时也可以进行相应的简写。
5.2 参数类型推断写法说明
- 参数类型是可以推断时,可以省略参数类型。
- 当传入的函数,只有单个参数时,可以省去括号。
- 如果变量只在
=>
右边只出现一次,可以用_
来代替。
5.3 案例
val list = List(1, 2, 3, 4)
println(list.map((x:Int)=>x + 1)) //(2,3,4,5)
println(list.map((x)=>x + 1)) //(2,3,4,5)
println(list.map(x=>x + 1)) //(2,3,4,5)
println(list.map( _ + 1)) //(2,3,4,5)
def f1(n1:Int ,n2:Int): Int = {
n1 + n2
}
println(list.reduce(f1)) // 10
println(list.reduce((n1:Int ,n2:Int) => n1 + n2)) //10
println(list.reduce((n1 ,n2) => n1 + n2)) //10
println(list.reduce(_ + _)) //10
6. 闭包
6.1 基本介绍
- 闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)。
6.2 案例
def minusxy(x: Int) = (y: Int) => x - y
//f 函数就是闭包.
val f = minusxy(20)
println("f(1)=" + f(1)) // 19
println("f(2)=" + f(2)) // 18
(y: Int) => x – y
返回的是一个匿名函数 ,因为该函数引用到到函数外的x
,那么该函数和x
整体形成一个闭包。 如:这里val f = minusxy(20)
的f
函数就是闭包。- 可以这样理解,返回函数是一个对象,而 x 就是该对象的一个字段,他们共同形成一个闭包。
- 当多次调用
f
时(可以理解多次调用闭包),发现使用的是同一个x
, 所以x
不变。 - 在使用闭包时,主要搞清楚返回函数引用了函数外的哪些变量,因为他们会组合成一个整体(实体),形成一个闭包。
7. 函数柯里化
7.1 基本介绍
- 函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化。
- 柯里化就是证明了函数只需要一个参数而已。
7.2 案例
// 编写一个函数,接收两个整数,可以返回两个数的乘积
// 使用常规的方式完成
def mul(x: Int, y: Int) = x * y
println(mul(10, 10))
// 使用闭包的方式完成
def mulCurry(x: Int) = (y: Int) => x * y
println(mulCurry(10)(9))
// 使用函数柯里化完成
def mulCurry2(x: Int)(y:Int) = x * y
println(mulCurry2(10)(8))
8. 控制抽象
8.1 基本介绍
- 参数是函数。
- 函数参数没有输入值,也没有返回值。