函数式编程
不同范式对比:
- 面向过程:按照步骤解决问题。
- 面向对象:分解对象、行为、属性,通过对象关系以及行为调用解决问题。耦合低,复用性高,可维护性强。
- 函数式编程:面向对象和面向过程都是命令式编程,但是函数式编程不关心具体运行过程,而是关心数据之间的映射。纯粹的函数式编程语言中没有变量,所有量都是常量,计算过程就是不停的表达式求值的过程,每一段程序都有返回值。不关心底层实现,对人来说更好理解,相对地编译器处理就比较复杂。
- 函数式编程优点:编程效率高,函数式编程的不可变性,对于函数特定输入输出是特定的,与环境上下文等无关。函数式编程无副作用,利于并行处理,所以Scala特别利于应用于大数据处理,比如Spark,Kafka框架。
object Scala01Function { def main(args: Array[String]): Unit = { //TODO 函数式编程语言 //1.scala中的函数并不是数学中函数 // 这里的函数其实表示的就是功能的封装,java中功能的封装一般称之为方法 //2.java中方法其实在scala中就是函数,但是声明在类中的函数称为方法 //函数只在当前作用域有效,但是方法需要受到类的约束 //3.因为函数其实就是功能的封装,也就是意味着,功能的名称不能重复,也不能重写,重载的概念 //因为方法属于类,那么就可能存在方法的重载,重写 val s="zhangsan" val newString=s.substring(0,1).toUpperCase()+s.substring(1) println(newString) val s1="list" //TODO 函数的声明和使用 //def:scala语言声明方法的关键字 //声明=> def 函数名(参数列表):返回值类型={ 函数体 } //使用=> 函数名(参数) def test():Unit={ println("test...") } test() } }
package 函数式编程 object Scala02Function { def main(args: Array[String]): Unit = { //TODO 函数式编程语言 //1.函数可以声明在任何的位子,方法只能声明在类中 def test():Unit={ println("test function...") } //如果函数名称和方法名称相同,那么默认情况下,会调用函数。如果没有函数,那么调用方法 //方法和对象相关,而函数是独立使用的。 this.test() } //方法 def test():Unit={ println("tst method...") } }
函数&方法
object Scala03Function { def main(args: Array[String]): Unit = { //TODO 函数式编程语言 //函数的本质就是java中的方法 //scala源码中,方法就是函数,编译后的字节码中,函数就是方法 //函数编译成方法中,增加了修饰符:private static final //函数编译成功时,函数名会自动发生变化,为了避免和方法名冲突 def test(): Unit = { println("test....") } test() } def test():Unit={ println("test method....") } }
object Scala04Function { def main(args: Array[String]): Unit = { //TODO函数式编程语言 //函数应用功能,说的简单点就是声明和使用 //函数其实就是功能的封装,就意味着功能已经按照特定的规则封装好了,所以重点在如何调用 def headerUpper(s:String):String={ return s.substring(0,1).toUpperCase()+s.substring(1) } val newString:String=headerUpper("lisi") println(newString) //函数式编程侧重点:函数名,输入参数,返回结果 //TODO函数声明 } }
函数参数
package 函数式编程 object Scala04Function { def main(args: Array[String]): Unit = { //TODO函数式编程语言 //函数应用功能,说的简单点就是声明和使用 //函数其实就是功能的封装,就意味着功能已经按照特定的规则封装好了,所以重点在如何调用 def headerUpper(s:String):String={ return s.substring(0,1).toUpperCase()+s.substring(1) } val newString:String=headerUpper("lisi") println(newString) //函数式编程侧重点:函数名,输入参数,返回结果 //TODO函数声明 //1. 无参无返回值 def fun1():Unit={ println("fun1...") } fun1() //2. 无参有返回值 def fun2():String={ return "fun2..." } println(fun2()) //3. 有参无返回值 def fun3(name:String):Unit={ println(s"fun3....$name") } fun3("zhangsan") //4. 有参有返回值 def fun4(name:String):String={ return name.toUpperCase } val str: String = fun4("sunyujie") println(str) //5. 多参数无返回值 def fun5(name:String,age:Int):Unit={ println( s"name=$name,age=$age") } fun5("zhagnsan",32) //6. 多参数有返回值 def fun6(name:String,age:Int):String={ return s"name=$name,age=$age" } println(fun6("sss",20)) } }
object ScalaFunction_Normal { def main(args: Array[String]): Unit = { //TODO函数参数是没有限制个数的,但是个数越多,传值越麻烦,所以并不推荐 def fun1( name1:String, name2:String, name3:String ):Unit={ } //TODO 参数个数简化 //1. 可变参数,类似于java中的参数... // Java : String ... args // Scala : args : String* //可变参数因为不确定,所以底层实现时,会采用不同的数组集合实现 def fun2(names: String*): Unit = { println(names) } fun2() //List() fun2("Zhangsan")//ArraySeq(Zhangsan) fun2("zhangsan","lisi") //TODO 可变参数位子 // 可变参数只能有一个且要写到参数列表的最后面.:参数列表最后一项 def fun3(password:String,name:String*):Unit={ } fun3("zhangsan","lisi","123123") } }
参数默认值
package 函数式编程 //TODO 函数式编程语言 -普通版 //希望参数有默认值;如果不传递,那么参数自动取默认值 //Scala实现不了这个操作,函数的参数默认使用val声明 //下面的代码存在的问题: //1.参数传值为null,并不是没有传值,只是传递一个特殊值 //2.在一个函数中,有相同含义的多个变量,容易出现歧义 object ScalaFunction_Normal_1 { def main(args: Array[String]): Unit = { def test(name:String,password:String):Unit={ var newpassword=password if(password==null){ newpassword="0000000" } println(s"注册用户:${name},密码:${newpassword}") } //TODO 在参数声明时,进行初始化 def test1(name: String, password: String="00000"): Unit = { var newpassword = password if (password == null) { newpassword = "0000000" } println(s"注册用户:${name},密码:${newpassword}") } test("lisi",null) test("lisi","1234563") test1("wangwu","123456") } }
带名参数
object ScalaFunction_Normal_2 { def main(args: Array[String]): Unit = { def fun3(name: String, password: String = "000000", address: String): Unit = { println(s"name = $name , password = $password , address = $address") } fun3("wangwu", "123456", "beijing") //如果想要改变参数的传值顺序,那么可以采用带名参数 fun3("wangxiaowu", address = "beijing") } }
函数式编程 - 至简原则(能省则省)
package 函数式编程 object ScalaFunction_NightMare { def main(args: Array[String]): Unit = { //如果编译器可以动态识别的语法,那么开发人员可以不需要编码,可以省略 // TODO 1)省略return关键字 def fun1(): String = { return "zhangsan" } println(fun1()) def fun11(): String = { "zhangsan" } println(fun11()) // TODO 2)省略花括号 如果逻辑代码只有一行,那么可以将大括号省略 def fun2():String="zhangsan" println(fun2()) //TODO 3)省略返回值类型:如果能够通过返回值推断返回值类型,那么返回值类型可以省略 def fun3() = "zhangsan" println(fun3()) //TODO 4)省略参数列表:如果函数的参数列表中没有声明参数,那么小括号可以省略 //如果省略参数列表的小括号,那么调用时也不能添加 //因为省略了很多语法内容,所以变量声明和函数声明很像,所以必须采用关键字区分 def fun4="zhangsan" println(fun4) //TODO 5)省略等号:如果函数体中有明确的return语句,那么返回值类型不能省略 def fun5():Unit={ return "张三" } // 如果函数体返回值类型明确为Unit, 那么函数体中即使有return关键字也不起作用 def fun51(): Unit = { return "zhangsan" } // 如果函数体返回值类型声明为Unit, 但是又想省略,那么此时就必须连同等号一起省略 def fun52() { return "zhangsan" } println(fun52()) println(fun5()) println(fun51()) // TODO 6)省略名称和关键字:如果函数名称不重要的时候,def和函数名也可以省略,称为匿名函数 //1.def和函数名省略 //2.返回值类型也需要省略,由逻辑代码自动推断 //3.等号需要增加大于浩表示关联 ()=>{ return "zhagnsan" } } }
函数就是对象
object ScalaFunction_Hell { def main(args: Array[String]): Unit = { // TODO 函数式编程语言--地狱版 /* * Scala语言是完全面向对象的语言,所以万物皆对象 * Scala语言是完全面向函数式编程语言,所以万物皆函数 * 函数也是对象,对象也是函数 * java: * class User{ * private String name; * } * User user=new User() * * scala:声明一个函数,等同于声明一个函数对象 * def test(){ * * } */ def test():Unit={ } // 如果一个函数声明时,参数列表中没有参数,那么调用时可以省略小括号 println(test) //打印test方法执行结果 //如果不想让函数执行,只是想访问这个函数本身,可以采用特殊符号进行转化 println(test _) //对象 } }
函数类型&函数参数个数
package 函数式编程 object ScalaFunction_Hell01 { //函数其实就是对象 //1.对象应该有类型 //2.对象应该可以赋值给其他人使用 def test():Unit={ } //将函数对象test赋值给f //此时,f可以通过编译器自动推测类型,函数对象类型称之为函数类型 //Funcation0[Unit]: //这里类型中的0表示函数参数列表中参数的个数 //中括号中的Unit表示函数没有返回值 val f:Function0[Unit]=test _ def test1(age:Int):String={ age.toString } //Funcation1[Int,String] /* * 这里类型的1表示函数参数列表中的参数的个数 * 中括号中的Int表示函数参数的类型 * 中括号中的String表示函数返回值类型 * */ val j:Function1[Int,String]=test1 _ //TODO [函数对象]的参数最多只有22个 //TODO 为了使用方便,函数类型可以使用另外一种声明方式 //这里的函数类型为:Int(参数列表的参数类型)=>String(返回值类型) def test3(name:String,age:Int):Unit={ } //(String,Int)=>Unit val f3=test3 _ f3 }
函数对象的使用
package 函数式编程 object ScalaFunction_Hell02 { def main(args: Array[String]): Unit = { //TODO 函数式编程语言 def test():Unit={ println("test...") } //将函数对象赋值给一个变量,那么这个变量就是函数 //既然这个变量就是函数,所以这个变量可以传参后执行 val f=test _ println(f) //对象 f() } }
将函数对象作为参数使用
package 函数式编程 object ScalaFunction_Hell03 { def main(args: Array[String]): Unit = { /* * TODO java: * public void test(User u){ * u.xxx(); * } * User user=new User(); * test(user) * */ //TODO 将函数对象作为参数使用 def fun():Unit={ println("test...") } def test(f:()=>Unit):Unit={ f() } //将函数对象赋值给函数 val f=fun _ test(f) } }
package 函数式编程 object ScalaFunction_Hell04 { def main(args: Array[String]): Unit = { def sum(x:Int,y:Int)={ x+y } def diff(x: Int, y: Int) = { x -y } def test(f:(Int,Int)=>Int):Unit={ val result: Int = f(10, 20) println(result) } //将函数对象作为参数使用,类似于将逻辑进行传递,意味着逻辑可以不用写死 //TODO 下划线的省略 //将函数名称直接作为参数传递给另一个函数,此时,不需要使用下划线的 //使用下划线的目的是不让函数执行,而是将它作为对象使用 //TODO 函数名的名称真的重要吗?那么参数名称很重要 test(sum) test(diff) } }
将函数对象作为参数使用--匿名函数
package 函数式编程 object ScalaFunction_Hell05 { def main(args: Array[String]): Unit = { //如果函数名称不重要,那么在传参时,就可以省略函数名称 def test(f: (Int, Int) => Int): Unit = { val result: Int = f(10, 20) println(result) } def sum(x:Int,y:Int):Int={ x+y } //test(sum) //TODO 如果函数没有名称和def,那么是匿名函数,一般就是作为参数使用 test( (x:Int,y:Int)=>{ x+y } ) //TODO 匿名函数作为参数使用时,可以采用[至简原则] //1.匿名函数的逻辑代码只有一行,那么大括号可以省略 test( (x:Int,y:Int)=>x+y ) //2.匿名函数的参数类型如果可以推断出来,那么参数类型可以省略 test( (x,y)=>x+y ) //3.匿名函数中如果参数列表个数只有一个,那么小括号可以省略 //4.匿名函数如果参数按照顺序只执行一次的场合,那么可以使用下划线代替参数,省略参数列表 test(_+_) //这里的第一个下划线就可以表示第一个参数,依次类推,数值表示任意 def test1(f:(String)=>Unit):Unit={ f("张三") } def fun(name:String):Unit={ println(name) } //把名称和关键字省略 test1( (name: String)=> { println(name) } ) //简单原则 test1( // name => println(_) println(_) ) test1(println) } }
匿名函数小练习
package 函数式编程 object ScalaFunction_Hell05_Test { def main(args: Array[String]): Unit = { def calc(x:Int,f:(Int,Int)=>Int,y:Int)={ f(x,y) } def +(x:Int,y:Int):Int={ x+y } def -(x: Int, y: Int): Int = { x - y } // val result=calc(5,+,3) // println(result) val result=calc(5, _+_,9) println(result) } }
函数作为返回值
package 函数式编程 object ScalaFunction_Hell06 { def main(args: Array[String]): Unit = { //1.将函数对象作为变量赋值使用 //2.将函数对象作为参数使用 /*TODO java: public User test(){ User user=new User(); return user; } User u=test() u.xxx u.yyy * */ //TODO 3.scala也可以将函数对象作为返回结果返回 //函数的返回值类型一般情况下不声明,使用自动推断 def outer()={ //函数类型 def inner():Unit={ println("inner...") } //构建对象 inner _ } //此时,f就是一个函数对象,有函数类型:()=>Unit val f=outer() f() //对象加个()就能执行 } }
函数作为返回值练习
package 函数式编程 object ScalaFunction_Hell07 { def main(args: Array[String]): Unit = { //1.将函数对象作为变量赋值使用 //2.将函数对象作为参数使用 /*TODO java: public User test(){ User user=new User(); return user; } User u=test() u.xxx u.yyy * */ //TODO 3.scala也可以将函数对象作为返回结果返回 //函数的返回值类型一般情况下不声明,使用自动推断 // def outer() = { //函数类型 // def inner(): Unit = { // println("inner...") // // } //构建对象 // // inner _ // } //此时,f就是一个函数对象,有函数类型:()=>Unit // val f = outer() // f() //对象加个()就能执行 //javascript:函数式 // outer()() def outer(x:Int) = { //函数类型 def mid(f:(Int,Int)=>Int)={ def inner(y:Int)= { f(x,y) } //构建对象 inner _ } mid _ } // val mid=outer(10) // val inner = mid(_ + _) // val result = inner(20) // println(result) println(outer(10)(_+_)(20)) } }
闭包
package 函数式编程 object ScalaFunction_Hell08 { //TODO 如果一个函数使用了外部的变量,但是改变这个变量的生命周期 //将这个变量包含到当前函数的作用域内,形成闭包的效果(环境),这个环境称为闭包环境 //简称闭包 def outer(x:Int)={ def inner(y:Int)={ x+y } inner _ 返回 } val inner=outer(10) val result=inner(20) println(result) }
思考一个问题: 没有使用外部变量还能称之为闭包吗?
package 函数式编程 object ScalaFunction_Closure { //TODO 闭包 //scala在2.12版本前,闭包使用匿名函数类实现 //scala在2.12版本后,闭包使用的是改变函数声明完成的 def main(args: Array[String]): Unit = { def outer(a:Int)={ def inner(b:Int)={ a+b } inner _ } var inner=outer(10) inner(20) val name="张三" def test():Unit={ println(name) } val f =test _ //对象 f() //生命周期改变了,所以是闭包 //总结闭包场景: //1.内部函数使用了外部的数据,改变了生命周期 //2.将函数作为对象调用,改变函数本身的生命周期 //3.所有匿名函数都有闭包 //4.内部函数返回外部使用也会有闭包 } }
控制抽象
package 函数式编程 import scala.util.control.Breaks object ScalaFunction_Abstract { // 抽象:不完整 //抽象类:不完整的类 //抽象方法:不完整的方法 def main(args: Array[String]): Unit = { def test(f:()=>Unit):Unit={ f() } //函数类型只有返回,没有输入场合,称为抽象,因为不完整 //调用的时候,不能使用括号 //在传值的时候,就需要进行控制 def test1(f: =>Unit):Unit={ f } test( ()=>{ println("test....") } ) //TODO 完整的参数传递,是将函数对象作为参数进行传递 //TODO 所谓的控制抽象,其实就是将对象作为参数进行传递 //自定义语法时,可以采用控制抽象,因为代码是可以传递,也就意味着逻辑是变化的 test1( println("test1...") ) //TODO 循环中断的代码就体现了控制抽象 Breaks.breakable{ //breakable底层是把下面的代码作为参数 for(i<-1 to 5){ if(i==3){ Breaks.break() } println("i= "+i) } } } }
函数柯里化
package 函数式编程 object ScalaFunction_Curry { def main(args: Array[String]): Unit = { //TODO 函数式编程语言-函数柯西化 def test(a:Int,b:Int):Unit={ for (i<-1 to a){ println(i) } for (i<-1 to b){ println(i) } } //函数的参数之间可能没有关系,那么如果在传值的时候,同时传递,其实就有耦合性,而且增加了传参的难度 val a=10//10min val b=20 //60min test(a, b) //柯西化,就是为了函数简单化,将无关的参数进行分离,可以设定多个参数列表 def test1(a: Int)( b: Int): Unit = { for (i <- 1 to a) { println(i) } for (i <- 1 to b) { println(i) } } val intToUnit:Int=>Unit=test1(10) test1(10)(20) //等同于 def outer()={ def inner():Unit={ println("inner...") } inner _ } val inner: () => Unit = outer() inner() println("==================") outer()() } }
递归函数
package 函数式编程 object Scala05Functiondigui { def main(args: Array[String]): Unit = { //TODO 递归:自己调用自己,应该跳出递归逻辑 //StackOverflowError:栈溢出 //Java在执行方法调用时,会将方法进行压栈处理,方法执行完毕后,会有弹栈处理 //如果方法没有执行完,又调用了其他方法,那么方法栈空间会下压 //如果越压越多,那么栈空间不够了,所以会发生溢出操作 //TODO 阶乘 //Scala中的返回类型不能省略 def test(num: Int): Int = { if (num <= 1) { 1 } else { num * test(num - 1) } } println(test(5)) //TODO Java的栈内存有大小限制的 // 方法执行时,压栈的内存也是有大小的,那么栈内存不可能无限压栈 //如果压栈的次数超过阀值,就会出现错误,即使有跳出的逻辑也会出错 //scala采用了一种特殊的语法优化递归操作,尾(伪)递归 //scala采用while循环实现伪递归 //java中的尾递归没有优化 def test1():Unit={ println("test1..") //弹栈 test1() } } }
栈内存溢出&栈溢出
//5M =10*1024K //2G=2*1024M=200线程 //TODO 栈内存溢出:没有足够的内存分配栈空间(内存) //java中的内存,堆内存,方法区内存只有一个,但是栈内存可以是多个 //一个线程就是一个独立栈内存
惰性函数
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
package 函数式编程 object Scala06Functionlaze { def main(args: Array[String]): Unit = { //TODO 惰性函数:函数是否在调用时马上执行 def test(): String = { println("function...") "zhangsan" } val a = test() println("----------") println(a) } }
package 函数式编程 object Scala06Functionlaze { def main(args: Array[String]): Unit = { //TODO 惰性函数:函数是否在调用时马上执行 def test(): String = { println("function...") "zhangsan" } lazy val a = test() println("----------") println(a) } }
函数式编程-小练习
将简化版本的函数式编程代码恢复完整代码
//test(10,20,_+_) //30
package 函数式编程 object Scala07Function_test { def main(args: Array[String]): Unit = { //TODO 将简化版本的函数式编程代码恢复完整代码 //test(10,20,_+_) //30 def fun(x:Int,y:Int):Int={ x+y } def test(x:Int,y:Int,f:(Int,Int)=>Int)= { f(x,y) } println(test(10,20,fun)) } }
//test1(_.substring(0,1))
package 函数式编程 object Scala07Function_test { def main(args: Array[String]): Unit = { //TODO 将简化版本的函数式编程代码恢复完整代码 //test(10,20,_+_) //30 def fun(x:Int,y:Int):Int={ x+y } def test(x:Int,y:Int,f:(Int,Int)=>Int)= { f(x,y) } println(test(10,20,fun)) //test1(_.substring(0,1)) // test1(()=>{_.substring(0,1)}) def fun1(name:String):String={ name.substring(0,1) } def test1(f:(String)=>String):String={ f("张三") } // test1((x:String)=>{x.substring(0,1)}) println(test1(fun1)) } }
//test2(_*2)
def fun2(x: Int): Int = { x*2 } def test2(f:(Int)=>Int):Int={ f(10) } // test2((x:Int)=>{ // x*2 // }) println(test2(fun2))
控制抽象可以实现自定义语法
package 函数式编程 object Scala08Function_test { def main(args: Array[String]): Unit = { //TODO 控制抽象可以实现自定义语法 /* * 自定义while循环 * while(条件表达式){ * 循环体 * } * */ def mywhile(cond : => Boolean)(op : =>Unit):Unit={ if(cond){ op mywhile(cond)(op) } } val age=30 mywhile(age==30){ println("age=30") } } }