第5章 函数式编程
5.1 函数式编程思想
- 在之前的学习中,我们一直学习的就是面向对象编程,所以解决问题都是按照面向对象的方式来处理的。比如用户登陆,但是接下来,我们会学习函数式编程,采用函数式编程的思路来解决问题。scala编程语言将函数式编程和面向对象编程完美地融合在一起了。
- 面向对象编程
分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题
-
函数式编程
将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
5.2 函数基础
-
基本语法
-
函数与方法的区别
package chapter05 object Test01_FunctionAndMethod { // main方法,方法和函数差不多,可以统称为函数 // 函数定义在任何地方,方法定义在类内 def main(args: Array[String]): Unit = { // 1. 定义函数 def sayHi(name: String): Unit = { println("hi, " + name) } // 调用函数 sayHi("scc") // 2. 调用对象的方法 Test01_FunctionAndMethod.sayHi("scc") val result = Test01_FunctionAndMethod.sayHello("Alice") println(result) } // 2. 定义对象的方法 def sayHi(name: String): Unit = { println("Hi, " + name) } def sayHello(name: String): String = { println("Hi, " + name) "Hello" } }
-
函数的定义,六种情况
package chapter05 object Test01_FunctionDefine { def main(args: Array[String]): Unit = { // 定义函数 def f1(): Unit = { println("1. 无参,无返回值") } println(f1()) def f2(): Int = { println("2. 无参,有返回值") 2 } println(f2()) def f3(name: String): Unit = { println("3. 有参,无返回值" + name) } println(f3("scc")) def f4(name: String): Int = { println("4. 有参,有返回值") 2 } println(f4("scc")) def f5(name1: String, name2: String): Int = { println("5. 多参,有返回值" + name1 + name2) 2 } // f5("scc", "zyy") println(f5("scc", "zyy")) def f6(name1: String, name2: String): Unit = { println("6. 多参,无返回值" + name1 + name2) } println(f6("scc", "zyy")) } }
-
函数参数,4种用法
package chapter05 //函数参数特殊用法,四种 object Test03_FunctionParameter { def main(args: Array[String]): Unit = { // 1. 可变参数 def f1(str: String*): Unit = { println(str) } f1("scc") f1("scc", "zyy", "520") // 2. 多个参数,可变参数在后 def f2(str1: String, str2: String*): Unit = { println("str1: " + str1 + " str2: " + str2) } f2("scc") f2("scc", "zyy", "520") // 3. 参数默认值,放在最后 def f3(name: String = "atNanJing"): Unit = { println("name: " + name) } f3() // 4. 带名参数 def f4(name: String = "zyy", age: Int): Unit = { println(s"name: ${name} age is ${age}") } f4(name = "scc", age = 21) f4(age = 21) } }
-
函数至简原则
所谓的至简原则,其实就是Scala的编译器为了提高开发效率。帮助我们将函数声明中能简化的地方全部都进行了简化。也就是说将函数声明中那些能省的地方全部都省掉。所以简单来说就是:能省则省
package chapter05 object Test04_Simplify { def main(args: Array[String]): Unit = { // 定义函数 def f0(name: String): String = { return name } println(f0("scc")) // 1. 省略return def f1(name: String): String = { name } println(f1("scc")) // 2. 如果函数体只有一行代码,则省略花括号 def f2(name: String): String = name println(f2("scc")) // 3. 函数返回值如果能推断出来,则省略返回值类型,相当于数学函数f3(x)=x def f3(name: String) = name // 4. 如果定义了return,则不能省略返回值类型 def f4(name: String): String = { return name } // 5. 如果函数声明为Unit,那么即使函数体使用return关键字,也不起作用 def f5(name: String): Unit = { return name } // 6. 如果Scala期望无返回值类型,则等号可以省略,叫做过程 def f6(name: String) { println(name) } // 7. 如果函数没有参数,但是声明的时候加上了小括号,那么调用的时候,小括号可加可不加 def f7() { println("7. 如果函数没有参数,但是声明的时候加上了小括号,那么调用的时候,小括号可加可不加") } f7() f7 // 8. 如果函数没有参数,那么声明的时候可以省略小括号,在调用的时候,小括号不能加 def f8 { println("8. 如果函数没有参数,那么声明的时候可以省略小括号,在调用的时候,小括号不能加") } f8 // 9. 如果不关系函数名称,只关心逻辑处理,则可以省略def和函数名称,此时为匿名函数或者lambda表达式,区别为=> (name: String) => { println(name) } } }
5.3 高阶函数-匿名函数
-
匿名函数lambda及简化原则
package chapter05 object Test05_Lambda { def main(args: Array[String]): Unit = { // 1. 函数类型,String => Unit val fun: String => Unit = (name: String) => { println(name) } fun("atguigu") println("===================================") // 2. 定义一个函数,以匿名函数作为参数传入 def f(func: String => Unit): Unit = { func("匿名函数") } f(fun) // 3. 匿名函数作为参数传入,类似于传入数据处理逻辑op,处理已经准备好的数据 f((name: String) => { println(name) }) // 4. 匿名函数至简原则,匿名函数,参数类型可以省略,会根据形参自动推断 f((name) => { println(name) }) // 5. 匿名函数至简原则,匿名函数,只有一个参数,省略括号 f(name => { println(name) }) //6. 匿名函数至简原则, 匿名函数,函数体只有一行,大括号可以省略 f(name => println(name)) // 6. 匿名函数至简原则,匿名函数,如果参数只出现一次,则参数和lambda表达式箭头=>可以省略,并且后面参数用_代替 f(println(_)) // 7. 如果能够判断println是一个函数体,而不是调用语句,则省略下划线 f(println) // 实际示例,定义一个“二元运算”函数,只操作1和2两个数,但是具体运算通过参数传入 def dualFunctionOneAndTwo(func: (Int, Int) => Int): Int = { func(1, 2) } dualFunctionOneAndTwo((num1, num2) => { num1 + num2 }) dualFunctionOneAndTwo(_ * _) } }
-
函数高阶操作
- 函数作为值进行传递,注意下划线_,此时为function对象
- 函数作为参数进行传递
- 函数作为函数返回值进行返回
package chapter05 // 函数作为值进行传递 object Test06_HighOrderFunction { def main(args: Array[String]): Unit = { def f(n: Int): Int = { println("f调用") n + 1 } def fun(): Int = { println("fun调用") 1 } val result: Int = f(123) println(result) //1. 函数作为值进行传递,注意下划线_,此时为function对象 val f1: Int => Int = f val f2 = f _ println(f1) println(f1(1)) println(f2) println(f2(1)) // 函数赋值 val f3: () => Int = fun val f4: () => Int = fun _ println(f3) println(f4) // 2. 函数作为参数进行传递 // 定义二元运算参数 def dualEval(op: (Int, Int) => Int, a: Int, b: Int): Int = { op(a, b) } println(dualEval((a, b) => { a + b }, a = 1, b = 2)) println(dualEval((a, b) => { a - b }, a = 1, b = 2)) // 3. 函数作为函数返回值进行返回 def f5() = { def f6(a: Int) = { println("f6调用" + a) } f6 _ //将函数直接返回,而不是函数调用 } val f7 = f5() println(f7) println(f7(10)) // 函数科里化吧? println(f5()(10)) } }
-
函数高阶操作实践
package chapter05 object Test07_Practice_CollectionOperation { def main(args: Array[String]): Unit = { // 对数组进行处理,将操作抽象出来,处理完毕之后的结果返回一个新的数组 val arr = Array(1, 2, 3, 4, 5) // 对数组进行处理,将操作抽象出来,处理完毕之后的结果返回一个新的数组 def arrayOperation(array: Array[Int], op: Int => Int): Array[Int] = { array.map(op) } // 定义一个加一操作 val fun = (num: Int) => { num + 1 } def addOne(num: Int): Int = { num + 1 } arrayOperation(arr, fun).foreach(println) arrayOperation(arr, addOne).foreach(println) } }
-
函数柯里化和闭包
闭包推荐使用柯里化写法
-
递归 pass
-
控制抽象
-
传值参数
-
传名参数,传入的是代码块
注意a:=>Int 表示传入代码块,返回值为Int
直接传入代码块
-
-
惰性加载 lazy
指
函数
前面加了lazy关键字,和控制抽象差不多,可能节约资源吧
例子:
当调用两次时,result值被计算出来,因此不需要再次调用,此处和控制抽象中传名参数不一样