第十三章 函数式编程-高级
13.1 偏函数(partial function)
13.1.1 提出一个需求,引出思考
给你一个集合 val list = List(1, 2, 3, 4, "abc"),请完成如下要求:
1、将集合 list 中的所有数字 +1,并返回一个新的集合。
2、要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5)。
13.1.2 解决方式-filter + map 返回新的集合
13.1.3 解决方式-模式匹配
两种方式的示例代码如下:
示例代码如下:
1package com.atguigu.chapter13 2 3/** 4 * 给你一个集合 val list = List(1, 2, 3, 4, "abc"),请完成如下要求: 5 * 1、将集合 list 中的所有数字 +1,并返回一个新的集合。 6 * 2、要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5)。 7 */ 8object PartialFunDemo01 { 9 def main(args: Array[String]): Unit = { 10 // 思路1:filter + map 方式解决 11 // 虽然可以解决问题,但是麻烦。 12 val list = List(1, 2, 3, 4, "abc") 13 // 先过滤,再map 14 list.filter(f1) 15 println(list.filter(f1).map(f2).map(f3)) // List(2, 3, 4, 5) 16 17 // 思路2:模式匹配 18 // 小结:虽然使用模式匹配比较简单,但是不够完美。 19 val list2 = list.map(addOne2) 20 println(list2) // List(2, 3, 4, 5, ()) 21 } 22 23 // 模式匹配 24 def addOne2(i: Any): Any = { 25 i match { 26 case x:Int => x + 1 27 case _ => 28 } 29 } 30 31 // 将 List 转为 Any 32 def f1(n: Any): Boolean = { 33 n.isInstanceOf[Int] 34 } 35 36 // 将 Any 转为 Int [map] 37 def f2(n: Any): Int = { 38 n.asInstanceOf[Int] 39 } 40 41 def f3(n: Int): Int = { 42 n + 1 43 } 44}
输出结果如下:
1List(2, 3, 4, 5) 2List(2, 3, 4, 5, ())
13.1.4 偏函数的基本介绍
13.1.5 偏函数的快速入门
示例代码如下:
1package com.atguigu.chapter13 2 3object PartialFunDemo02 { 4 def main(args: Array[String]): Unit = { 5 // 使用偏函数解决 6 val list = List(1, 2, 3, 4, "hello") 7 // 定义一个偏函数 8 // 1. PartialFunction[Any, Int] 表示偏函数接收的参数类型是 Any,返回的类型是 Int 9 // 2. isDefinedAt(x: Any) 如果返回 true,就会去调用 apply 构建对象实例,如果是 false,就过滤 10 // 3. apply 构造器,对传入的值 + 1,并返回(新的集合) 11 val addOne3 = new PartialFunction[Any, Int] { 12 def isDefinedAt(any: Any) = if (any.isInstanceOf[Int]) true else false 13 def apply(any: Any) = any.asInstanceOf[Int] + 1 // 将 any 显示转换成 Int 14 } 15 16 // 使用偏函数 17 // 说明:如果是使用偏函数,则不能使用 map,应该使用 collect 18 // 说明偏函数的执行流程 19 // 1. 遍历 list 集合的所有元素 20 // 2. 然后调用 val element = if(partialFun-isDefinedAt(list单个元素)) {partialFun-apply(list单个元素)} 21 // 3. 每得到一个符合要求的元素 element,放入到新的集合,最后返回 22 val list3 = list.collect(addOne3) 23 println(list3) // List(2, 3, 4, 5) 24 } 25}
输出结果如下:
List(2, 3, 4, 5)
13.1.6 偏函数的小结

13.1.7 偏函数的简写形式
示例代码如下:
1package com.atguigu.chapter13 2 3/** 4 * 偏函数简化形式 5 */ 6object PartialFunDemo03 { 7 def main(args: Array[String]): Unit = { 8 // 使用偏函数的简化形式解决 9 // 第一种简写形式: 10 def f2: PartialFunction[Any, Int] = { 11 case i: Int => i + 1 // case 语句可以自动转换为偏函数 12 case j: Double => (j * 2).toInt 13 } 14 15 val list2 = List(1, 2, 3, 4, 5.6, "hello").collect(f2) 16 println(list2) // List(2, 3, 4, 5, 11) 17 18 // 第二种简写形式: 19 val list3 = List(1, 2, 3, 4, "ABC").collect { case i: Int => i + 1 } 20 println(list3) // List(2, 3, 4, 5) 21 } 22}
输出结果如下:
1List(2, 3, 4, 5, 11) 2List(2, 3, 4, 5)
13.2 作为参数的函数
1、基本介绍

2、快速入门案例
示例代码如下:
1package com.atguigu.chapter13 2 3object FunParameterDemo01 { 4 def main(args: Array[String]): Unit = { 5 def plus(x: Int) = 3 + x 6 val result1 = Array(1, 2, 3, 4).map(plus(_)) 7 println(result1.mkString(",")) // (4,5,6,7) 8 9 // 1. 在 scala 中,函数也是有类型,比如 plus 就是 <function1> 10 println("puls的函数类型是" + (plus _)) 11 12 } 13}
输出结果如下:
14,5,6,7 2puls的函数类型是<function1>
3、应用案例小结

13.3 匿名函数
1、基本介绍

2、快速入门案例
示例代码如下:
1package com.atguigu.chapter13 2 3object AnonymouseFunctionDemo01 { 4 def main(args: Array[String]): Unit = { 5 // 对匿名函数的说明 6 // 1. 不需要写 def 函数名 7 // 2. 不需要写返回类型,使用类型推导 8 // 3. = 变成 => 9 // 4. 如果有多行,则使用 {} 包括 10 val triple = (x: Double) => 3 * x 11 println(triple) // <function1> 12 println(triple(3)) // 9.0 13 } 14}
输出结果如下:
1<function1> 29.0
3、练习题
请编写一个匿名函数,可以返回2个整数的和,并输出该匿名函数的类型。
示例代码如下:
1 val f1 = (n1: Int, n2: Int ) => { 2 println("匿名函数被调用") 3 n1 + n2 4 } 5 println("f1类型=" + f1) // f1类型=<function2> 6 println(f1(10, 30)) // 40
13.4 高阶函数(higher-order function)
1、基本介绍
能够接受函数作为参数的函数,叫做高阶函数 (higher-order function)。可使应用程序更加健壮。
2、高阶函数基本使用案例
扩展代码如下:
1package com.atguigu.chapter13 2 3object HigherOrderFunctionDemo01 { 4 def main(args: Array[String]): Unit = { 5 // test 就是一个高阶函数,它可以接收 f: Double => Double 和 f1: Double => Int 6 def test(f: Double => Double, f1: Double => Int, n1: Double) = { 7 f(f1(n1)) 8 } 9 10 // sum 是接收一个 Double,返回一个 Double 11 def sum(d: Double): Double = { 12 d + d 13 } 14 15 def mod(d: Double): Int = { 16 d.toInt % 2 17 } 18 19 val res = test(sum, mod, 5.0) 20 println("res=" + res) // 2.0 21 } 22}
输出结果如下:
res=2.0
3、高阶函数可以返回函数类型
示例代码如下:
1package com.atguigu.chapter13 2 3object HigherOrderFunctionDemo02 { 4 def main(args: Array[String]): Unit = { 5 // 说明 6 // 1. minusxy 是高阶函数,因为它返回匿名函数 7 // 2. 返回的匿名函数是 (y: Int) => x - y 8 // 3. 返回的匿名函数可以使用变量接收 9 def minusxy(x: Int) = { 10 (y: Int) => x - y // 匿名函数 11 } 12 13 // 分步执行 14 // f1 就是 (y: Int) => 3 - y 15 val f1 = minusxy(3) 16 println("f1的类型=" + f1) 17 println(f1(1)) // 2 18 println(f1(9)) // -6 19 20 // 也可以一步到位的调用(函数柯里化) 21 println(minusxy(4)(9)) // -5 22 } 23}
输出结果如下:
1f1的类型=<function1> 22 3-6 4-5
13.5 参数(类型)推断
1、基本介绍

2、应用案例
示例代码如下:
1package com.atguigu.chapter13 2 3object ParameterInferDemo01 { 4 def main(args: Array[String]): Unit = { 5 6 val list = List(1, 2, 3, 4) 7 println(list.map((x: Int) => x + 1)) // (2,3,4,5) 8 println(list.map((x) => x + 1)) // (2,3,4,5) 参数类型是可以推断时,可以省略参数类型。 9 println(list.map(x => x + 1)) // (2,3,4,5) 当传入的函数,只有单个参数时,可以省去括号。 10 println(list.map(_ + 1)) // (2,3,4,5) 如果变量只在=>右边只出现一次,可以用_来代替。 11 12 println(list.reduce(f1)) // 10 13 println(list.reduce((n1: Int, n2: Int) => n1 + n2)) // 10 14 println(list.reduce((n1, n2) => n1 + n2)) // 10 15 println(list.reduce(_ + _)) // 10 16 } 17 18 def f1(n1: Int, n2: Int): Int = { 19 n1 + n2 20 } 21}
输出结果如下:
1List(2, 3, 4, 5) 2List(2, 3, 4, 5) 3List(2, 3, 4, 5) 4List(2, 3, 4, 5) 510 610 710 810
3、应用案例小结

13.6 闭包(closure)
1、基本介绍

2、应用案例小结

3、闭包的最佳实践
示例代码如下:
1package com.atguigu.chapter13 2 3/** 4 * 请编写一个程序,具体要求如下: 5 * 编写一个函数 makeSuffix(suffix: String) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包 6 * 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg),则返回 文件名.jpg,如果已经有.jpg后缀,则返回原文件名。 7 * 要求使用闭包的方式完成。 8 * String.endsWith(xx) 9 */ 10object ClosureDemo01 { 11 def main(args: Array[String]): Unit = { 12 // 使用并测试 13 val f = makeSuffix(".jpg") 14 println(f("dog.jpg")) // dog.jpg 15 println(f("cat")) // cat.jpg 16 17 } 18 19 // 自定义的函数 20 def makeSuffix(suffix: String) = { 21 // 返回一个匿名函数,该匿名函数会使用外部函数的suffix,那么该函数和suffix整体形成一个闭包。 22 (filename: String) => { 23 if (filename.endsWith(suffix)) { 24 filename 25 } else { 26 filename + suffix 27 } 28 } 29 } 30}
输出结果如下:
1dog.jpg 2cat.jpg
4、闭包的好处
13.7 函数柯里化(curry)
1、基本介绍

2、函数柯里化快速入门案例
3、函数柯里化最佳实践
示例代码如下:
1package com.atguigu.chapter13 2 3/** 4 * 函数柯里化最佳实践 5 * 比较两个字符串在忽略大小写的情况下是否相等,注意,这里是两个任务: 6 * 全部转大写(或小写) 7 * 比较是否相等 8 * 针对这两个操作,我们用一个函数去处理的思想,其实也变成了两个函数处理的思想(柯里化) 9 */ 10object CurryDemo02 { 11 def main(args: Array[String]): Unit = { 12 // 方式1:简单的方式,使用一个函数完成 13 def eq(s1: String)(s2: String): Boolean = { 14 s1.toLowerCase == s2.toLowerCase 15 } 16 println(eq("hello")("HELLO")) 17 18 // 方式2:使用稍微高级的用法(隐式类):形式为 str.方法() 19 def eq2(s1: String, s2: String): Boolean = { 20 s1.equals(s2) 21 } 22 // 隐式类:该隐式类扩展了String对象的功能 23 implicit class TestEq(s: String) { 24 // 体现了将比较字符串的事情,分解成两个任务完成: 25 // 任务1:checkEq 完转换大小写 26 // 任务2.:f函数完成比较任务 27 def checkEq(ss: String)(f: (String, String) => Boolean): Boolean = { 28 f(s.toLowerCase, ss.toLowerCase) 29 } 30 } 31 val str1 = "hello" 32 println(str1.checkEq("HeLLO")(eq2)) 33 34 // 方式2的简化写法 35 str1.checkEq("HeLLO")((s1: String, s2: String) => { s1.equals(s2) }) 36 str1.checkEq("HeLLO")((s1: String, s2: String) => s1.equals(s2)) // 代码块只有一行代码时。可以省略{} 37 str1.checkEq("HeLLO")((s1, s2) => s1.equals(s2)) // 参数类型是可以推断时,可以省略参数类型。 38 str1.checkEq("HeLLO")(_.equals(_)) // 如果变量只在=>右边只出现一次,可以用_来代替。 39 } 40}
输出结果如下:
1true 2true
13.8 控制抽象函数
1、看一个需求

2、控制抽象基本介绍

3、快速入门案例
示例代码如下:
1package com.atguigu.chapter13 2 3object AbstractControlDemo01 { 4 def main(args: Array[String]): Unit = { 5 // myRunInThread 就是一个抽象控制函数,其参数是没有输入,也没有输出的函数 f1: () => Unit 6 def myRunInThread(f1: () => Unit) = { 7 new Thread { 8 override def run(): Unit = { 9 f1() 10 } 11 }.start() 12 } 13 14 myRunInThread { 15 () => 16 println("干活咯!5秒完成...") 17 Thread.sleep(5000) 18 println("干完咯!") 19 } 20 21 // 简写形式 22 def myRunInThread2(f1: => Unit) = { 23 new Thread { 24 override def run(): Unit = { 25 f1 26 } 27 }.start() 28 } 29 30 // 对于没有输入,也没有返回值函数,可以简写成如下形式: 31 myRunInThread2 { 32 println("干活咯!5秒完成...~~~") 33 Thread.sleep(5000) 34 println("干完咯!~~~") 35 } 36 37 } 38}
4、进阶用法:实现类似 while 的 mywhile 函数
示例代码如下:
1package com.atguigu.chapter13 2 3object AbstractControlDemo02 { 4 def main(args: Array[String]): Unit = { 5 var x = 10 6 while (x > 0) { 7 x -= 1 8 println("x=" + x) 9 } 10 11 // 说明:实现类似 while 的 mywhile 函数 12 // 1 函数名为 mywhile,实现了类似 while 循环的效果 13 // 2. condition: => Boolean 是一个没有输入值,返回 Boolean 类型函数 14 // 3. block: => Unit 没有输入值,也没有返回值的函数 15 def mywhile(condition: => Boolean)(block: => Unit): Unit = { 16 // 类似 while 循环,递归 17 if (condition) { 18 block // x=9 x=8 x=7 ... 19 mywhile(condition)(block) 20 } 21 } 22 23 x = 10 24 mywhile(x > 0) { 25 x -= 1 26 println("x=" + x) 27 } 28 29 } 30}
输出结果如下:
1x=9 2x=8 3x=7 4x=6 5x=5 6x=4 7x=3 8x=2 9x=1 10x=0 11----- 12x=9 13x=8 14x=7 15x=6 16x=5 17x=4 18x=3 19x=2 20x=1 21x=0
本文同步分享在 博客“SoWhat1412”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。