说明: 没有名字的函数就是匿名函数,可以直接通过函数字面量(λ表达式)来设置匿名函数,函数字面量定义格式如下。
普通函数定义(带名函数)
val add=(x:Int,y:Int)=>{x+y}
匿名函数定义
(x:Int,y:Int)=>{x+y}
普通函数调用(带名函数)
val add=(x:Int,y:Int)=>{x+y}
println(add(10,20)) //30
匿名函数调用
println(((x:Int,y:Int)=>{x+y})(10,20)) //30
或者使用 .apply()
println(((x:Int,y:Int)=>{x+y}).apply(10,20)) //30
匿名函数有什么用?
一般用于配合高阶函数
使用,作为另一个函数的参数。
普通函数:
def main(args:Array[String]):Unit={
// 带名函数
val add=(x:Int,y:Int)=>{x+y}
println(m1(4, 5, add)) // 9
}
def m1(x:Int,y:Int,func:(Int,Int)=>Int):Int={
func(x,y)
}
匿名函数:
def main(args:Array[String]):Unit={
println(m1(4, 5, (x:Int,y:Int)=>{x+y})) // 9
}
def m1(x:Int,y:Int,func:(Int,Int)=>Int):Int={
func(x,y)
}
当然也可以简写:
def main(args:Array[String]):Unit={
println(m1(4, 5, _+_)) // 9
}
def m1(x:Int,y:Int,func:(Int,Int)=>Int):Int={
func(x,y)
}
柯里化(Currying): 菜鸟教程的解释:指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。
意思是说,一个函数可以作为另一个函数的返回值,调用函数的过程中,就是会形成函数柯里化
案例:
def m1(x:Int):Int=>Int={
def m2(y:Int):Int={
x+y
}
m2
}
m2 是一个函数并且作为m1函数返回值,那么在调用过程中就会形成柯里化
;
def main(args:Array[String]):Unit={
// 调用m1 函数
val f1=m1(1)
// 此时 f1 是一个对象的引用, Int=>Int 类型的对象
println("f1引用:",f1) // (f1引用:,Demo02$$$Lambda$1/1867083167@6cd8737)
// 调用 m2 函数;因为m1 还需要执行 m2 函数,还能获取最终的结果。
val f2=f1(2)
println(f2) // 3
}
def m1(x:Int):Int=>Int={
def m2(y:Int):Int={
x+y
}
m2
}
当然以上可以进行简写
;
def main(args:Array[String]):Unit={
println(m1(1)(2)) // 3
}
def m1(x:Int):Int=>Int={
def m2(y:Int):Int={
x+y
}
m2
}
总结:
当你的代码类似于以上的场景时,在函数中调用另一个函数,这种过程就可以称为函数柯里化
。
补充:
像上面这种情况,还可以进行再优化,代码更简洁;
def main(args:Array[String]):Unit={
// 这样定义
def add(x:Int)(y:Int)=x+y
// 调用
println(add(1)(2))
}
闭包(Closures):
简单理解:在函数中访问函数外的成员
就可以称为闭包
;
def main(args:Array[String]):Unit={
val y=10
def add(x:Int):Int={
x+y
}
println(add(2))
}
像 add 函数,既可以称为闭包。 参考 菜鸟教程 深入理解 Scala 中的闭包(Closures)
所谓递归,就是一个函数内,被自身函数所调用,形成循环调用的现象称为递归调用
;
案例:经典的斐波拉契
def main(args:Array[String]):Unit={
/**
* 斐波拉契
* @param n 表示月份
* @return
*/
def fibonacci(n:Int):Int={
if(n<3) {
1
}else {
fibonacci(n-1)+fibonacci(n-2)
}
}
// 调用
println(fibonacci(12)) // 144
}
这是一个典型的递归实现方式。 规范与总结
递归
。死递归
,会造成系统崩溃。def fibonacci(n:Int):Int={} // :Int 必须指定
控制抽象不能单独定义
,只能作为方法的参数类型存在
,控制抽象代表的就是一个块表达式
, 后续这个控制抽象可以当做函数调用
。
案例:
def main(args:Array[String]):Unit={
def sayHello():Unit={
println("hello,world")
}
def abc(func:()=>Unit):Unit={
func()
}
abc(sayHello) // hello,world
}
函数abc
接收一个函数,该函数没有参数,无返回值。
然而 abc
却不能称为 控制抽象
;我们熟悉的Breaks.breakable
就是典型的控制抽象
。
def breakable(op: => Unit) {
try {
op
} catch {
case ex: BreakControl =>
if (ex ne breakException) throw ex
}
}
通过abc
对比 breakable
唯一不同的地方就是 breakable
不是()
而是空格
,以及未指定返回值为Unit
abc
def abc(func:()=>Unit):Unit={}
breakable
def breakable(op: => Unit) {}
控制抽象语法:函数名:空格
=>返回值类型(如:op: => Unit
)
于是我们依样画葫芦
重新改改
def main(args:Array[String]):Unit={
def sayHello():Unit={
println("hello,world")
}
def abc(func: =>Unit){
func
}
abc(sayHello) // hello,world
}
现在的abc
也算是一个合格的控制抽象
了。
使用控制抽象,实现while功能。
使用控制抽象,实现一个while
循环功能,应该是比较经典的案例吧(至少我看了很多篇文章都这么玩)。
不明白就造轮子,看看 while
格式。
while (布尔表达式){
循环条件
}
while (布尔表达式) ://可以理解为需要一个布尔类型的参数或表达式。 {循环条件}:就是具体的实现。
自定义函数:
def myWhile(bool: =>Boolean)(op: =>Unit){
if (bool) {
op // 执行{} 中的内容
myWhile(bool)(op) // 如果条件成立,继续执行下一次myWhile
}
}
使用函数柯里化
,实现while功能。
(bool: =>Boolean):接收循环条件。
(op: =>Unit):用于接收循环内容。
调用、运行
def main(args:Array[String]):Unit={
var i=0
myWhile(i<10)({
println(s"i==$i")
i+=1
})
}
运行结果
i==0
i==1
i==2
i==3
i==4
i==5
i==6
i==7
i==8
i==9
思考:myWhile(bool: =>Boolean) 能否改成 myWhile(bool:Boolean)?如下:
def myWhile(bool:Boolean)(op: =>Unit){
if (bool) {
op // 执行{} 中的内容
myWhile(bool)(op) // 如果条件成立,继续执行下一次myWhile
}
}
运行一下: 死循环了,一不注意就跑到了四十多万了。
i==452393
i==452394
i==452395
i==452396
i==452397
i==452398
i==452399
i==452400
i==452401
i==452402
i==452403
i==452404
i==452405
i==452406
咦?为什么会这样呢?控制抽象
是一个函数表达式,也就是说它是一个函数;函数只能等调用它的时候才会运行。而 bool:Boolean
是一个变量,运行之后将不会改变。当第一次运行i<10时结果为true
,那么即便是i不停的+=1,结果依旧为true。控制抽象
,无论运行多少次,都会重新调用该函数,重新运算结果。
我表达的可能不是很好,接下来用案例说明吧。
def main(args:Array[String]):Unit={
val a:Unit={
println("------")
}
def c(func: =>Unit){
func
func
func
}
c(a)
}
运行之后,结果是什么? 答案;a 只会运行一次,其原因 a 是一个变量,初始化完成后就不再运行。
------
若传递进来的是一个函数
def main(args:Array[String]):Unit={
def c(func: =>Unit){
func
func
func
}
def a():Unit={
println("------")
}
c(a())
}
若是一个函数,每次运行都会执行。
------
------
------
函数中声明类型为 bool:Boolean 表示 bool是一个参数,运行第一次(1<10)的时候,执行一次就得到boolean了 那值就固定了,bool就等于true,在之后的循环判断中,将不在改变属于
静态赋值
。 bool: =>Boolean:表示是一个控制抽象
是一个函数块
,是动态
的,每一次的运行,都会动态为其分配新的结果。
在设计模式中有一种设计模式叫单例模式
;单例模式又分为两种饿汉式
和懒汉式
,这两种模式都可以实现单例模式,但是在实现上又有些许不同。
饿汉式:会率先创建好实例,等待被调用,即使一直不被调用,该实例依旧存在,这样就会造成资源的浪费。
懒汉式:就很好解决饿汉式
的问题,只有在被调用的时候才去初始化实例。但是懒汉式
在多线环境下就有缺陷
,就不得不要其他的方式去修复这些缺陷,如双重检查
或其他的方式弥补它的缺陷(跑题了)。
在scala中可以通过关键字 lazy
实现懒加载
。
a 是一个普通的值,代码从小往下执行,完成了a的初始化,结果值为10。
b使用 lazy 修饰之后,表示它值为惰性求值
,只有等到最终被调用的时候才会被初始化。
可以看到:_initialized=false;表示未进行初始化
调整断点往下执行
可以看到:只有等到 b 被真正调用的时候,才会进行初始化(_initialized=true) _value=20
lazy 不能定义在函数上:
'lazy' modifier allowed only with value definitions
吐槽:真的不喜欢截图,看着贼难受。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。