网站建设与数据库管理,济南做网站软件,郑州巩义网站建设,大兴网站开发网站建设咨询scala高级函数一.函数至简原则二.匿名的简化原则三.高阶函数四.柯里化和闭包五.递归六.抽象控制七.惰性加载#x1f525;函数对于scala#xff08;函数式编程语言#xff09;来说非常重要#xff0c;大家一定要学明白#xff0c;加油#xff01;#xff01;#xff01;…
scala高级函数一.函数至简原则二.匿名的简化原则三.高阶函数四.柯里化和闭包五.递归六.抽象控制七.惰性加载函数对于scala函数式编程语言来说非常重要大家一定要学明白加油一.函数至简原则
1.return可以省略Scala会把函数体最后一行代码作为返回值 def f0(name:String):String {name}println(f0(scala))2.如果函数体内只有一行代码可以省略花括号
def f2(name:String):Stringname3.如果返回值类型如果能推断出来和返回值类型可以一起省略
def f3(name:String)name这不就相当于数学上的函数嘛 f(x)x。 这也是至简原则的目的让函数最终尽可能符合我们数学上的使用习惯。
4.如果return没有省略则返回值类型不能省略
def f4(name:String){return name}
//会报错5.如果函数声明Unit(空类型)那么即使函数体中使用return也不起作用
def f5(name:String):Unit{return name}6.scala如果期望是无返回值类型可以省略等号,这种函数也叫做过程不存在映射关系
def f6(name:String){println(name)}7.如果函数无参数但是声明了参数列表那么调用时小括号可加可不加
def f7():Unit{println(name)}
f7()
f78.如果函数没有参数列表那么小括号可以省略调用时小括号必须省略
def f8:Unit{println(name)}
f89.如果不关心名称只关心逻辑处理那么函数名def可以省略,也不需要返回值。也就是匿名函数lambda表达式
(name:String) {println(name)}lambda表达式没有函数名如何调用呢
用法1直接定义个变量这样就相当于有了名字
val fun (name:String) {println(name)} //lambda表达式的返回值函数类型赋给fun 相当于有了名字
fun(scala)用法2定义一个函数以函数作为参数传入
//相当于函数的参数是固定的这个函数的操作却决于传入的函数
val fun (name:String) {println(name)}
def f(func:String Unit):Unit{ //String Unit 是函数的类型代表函数的参数是String类型返回类型是Unitfunc(scala)
}
f(fun)
f((name:String) {println(name)})匿名函数就是一个表达式它的返回值也就是这个表达式的值。
二.匿名的简化原则
1.参数类型可以省略会根据形参进行自动推导
f((name) {println(name)}) //看用法2中定义的函数传入的参数已经定义死了必须是String UnitString类型2.类型省略之后如果只有一个参数圆括号可以省略.
f(name{println(name)})3.匿名函数如果只有一行大括号也可以省略
f(nameprintln(name))4.如果参数只出现一次则参数省略且后面可以用_代替,注意对应顺序
f(println(_))5.如果可以推断出当前传入的println是一个函数体而不是调用语句可以直接省略下划线
f(println) //传入的是一个操作案例定义一个“二元运算”函数只对1和2两个数操作但是具体的运算通过传入的参数决定
def dualFunction(fun:(Int,Int)Int):Int{fun(1,2) //具体的参数已经是定死的
}
val add (a:Int,b:Int)ab
val minus (a:Int,b:Int)a-b
println(dualFunction(add))
println(dualFunction(minus))
println(dualFunction((a,b)a-b))
println(dualFunction(_-_))三.高阶函数
函数在scala编程里面是一等公民不想Java只能在类里面定义方法非常灵活在一个代码块中就可以定义函数调用函数。
1.函数可以作为值进行传递类似给函数起别名
def f(n:Int):Int{println(f调用)n1
}
val f1 f _ //f _ 代表f这整个函数体
val f2:IntInt f //这样f1函数对象就是函数类型f2:IntInt编译器知道要传函数所以可以只写个f2.函数作为参数进行传递
这里就是匿名函数案例演示的那种参数是定义死的具体操作看传入的函数参数
但是数据一定要定义死嘛我们可以函数参数和数据参数一起传入
def dualEval(op:(Int,Int)Int,a:Int,b:Int):Int{op(a,b)
}
def add(a:Int,b:Int):Int{ab
}
println(dualEval(add,12,35)) //把普通函数作为参数3.函数作为函数的返回值返回
看到这里我们可以发现只要是值用到的地方我们都可以用函数进行代替。
这里就设计到函数嵌套了
def f5():Int Unit{ //Int Unit这里说明返回值是函数类型def f6(a:Int){println(f6调用6)}f6 //return 可以省略
}
println(f5()) //这里得到的是一个引用函数也就是得到的f6
println(f5()(25))**应用案例**对数组进行处理将操作抽象出来处理完毕之后的结果返回一个新的数组。也就是大数据map操作
yield:就是在for循环中每次循环都会产生一个值然后将每次产生的值保存最后组成一个集合。
val arr:Array[Int]Array(12,45,75,98) //每次只对数组中的一个元素进行操作这个操作是单独抽象的只需要单独的定义操作def arrayOperations(array: Array[Int],op:IntInt):Array[Int]{for (elem - array) yield op(elem)}//定义一个加1操作def addOne(elem:Int):Int{elem1}//调用函数var newArray:Array[Int]arrayOperations(arr,addOne)println(newArray.mkString(,))//这里传入匿名函数也是可以的
var newArray2 arrayOperations(arr,_1 )
这里的应用是以后处理大数据来了一堆集合数据就是那些但是需要进行很多步操作 我们通过这样可以单独定义他 们的操作。
案例:通过函数嵌套的方式接受三个参数当这三种类型参数都为假时返回fales。 def f1(i:Int):String(CharBoolean){def f2(s:String):CharBoolean{def f3(c:Char):Boolean{if (i0sc0) false else true}f3}f2}
println(f1(0)()(0)) //结果为false
//可以用匿名函数进行简化书写,但是这种书写也会被函数的柯里化给代替
def func1(i:Int):String(CharBoolean){sc if (i0sc0) false else true
}四.柯里化和闭包 闭包如果一个函数访问到了它的外部局部变量的值那么这个函数和他所处的环境成为闭包 函数柯里化把一个参数列表的多个参数变成多个参数列表每个参数都是一个小括号 函数性编程语言scala定是支持闭包的。它可以延长他所使用的参数的做作用域如上面那个案例内层函数用到了外层函数的一个局部变量或者是一个参数为了我们在调用的时候还能分层调用在调用的时候第二步还能使用外层函数的参数那么闭包会把外层函数的参数或者局部变量和内层函数打包起来保存到一个函数对象里面存放在堆内存里面。
柯里化
def addCurrying(a:Int)(b:Int):Int{ab} //一旦使用了柯里化底层一定使用了闭包println(addCurrying(23)(32))}五.递归
递归一个函数/方法在函数/方法体内又调用了本身 方法调用自身 方法必须要有跳出的逻辑 方法调用自身时传递的参数应该有规律 Scala中递归必须声明返回值类型 //递归计算阶乘
def fact(n:Int):Int{if (n0) return 1 //这个return不能省因为scala只能自动返回最后一行这里不是最后一行fact(n-1)*n
}
//尾递归递归最后一行返回的只有对于自身的调用没有其他额外的计算这样当前这层函数不用保存任何东西这样就用压栈了节省空间。
def tailFact(n:Int):Int{tailrec //如果写的不是尾递归idea会报错def loop(n:Int,currRes:Int):Int{if(n0)return currResloop(n-1,currRes*n) //每次调用我不需要保存上一层的任何信息不用压栈做一个栈帧的覆盖就节省空间了。}loop(n,1)
}
//尾递归只有函数式编程语言才支持比如Java就不支持六.抽象控制
1.值调用把计算后的值传递过去
2.名调用把代码传递过去
//传值参数
def f0(a:Int):Unit{println(aa)
}def f1():Int{println(f1调用)12
}
f0(f1)
//传名参数,传递的不再是具体的值而是代码块
def f2(a:Int):Unit{ //注意这里参数的类型println(a:a)println(a:a)
}
f2(f1()) //每一次用到a的时候都会把f2中完整的代码块执行一遍。传名调用的案例自己实现一个while循环函数
//1.用闭包实现一个函数将代码块作为参数传入递归调用
def myWhile(condition:Boolean):(Unit)Unit{//内层函数需要递归调用参数就是循环体(代码块)def doLoop(op:Unit):Unit{ if(condition){opmyWhile(condition)(op) //尾递归} }doLoop _
}n10
myWhile(n1){println(n)n-1
}//参数是一个代码块时小括号可以省略//2.用匿名函数实现
def myWhile2(condition:Boolean):(Unit)Unit{//内层函数需要递归调用参数就是循环体(代码块)op{ if(condition){opmyWhile2(condition)(op) //尾递归} }
}
//3.用函数柯里化实现
def myWhile(condition:Boolean)(op:Unit):Unit{if(condition){opmyWhile3(condition)(op)}
}七.惰性加载
说明函数的返回值被声明lazy时函数的执行将被推迟直到我们首次对此取值该函数才会被执行这种函数我们称之为惰性函数。
不用到不执行加载
lazy val result:Int sum(13,47)
println(1.函数调用)
println(2.resultresult)def sum(a:Int,b:Int):Int{printlb(3.sum调用)
}//最后输出顺序时1 3 2