函数与闭包
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数与闭包相关的知识,希望对你有一定的参考价值。
scala中函数4种的表现形式{
对象中的函数,
内嵌在函数的函数,
函数字面量,
函数值
}
1)对象中的函数
不解释。
2)内嵌在函数中函数
控制helper函数的方式 一般限制访问级别private。scala可以通过内嵌方式控制(前者同样生效),类似方法中的变量(本地变量)。
本地函数可以访问外层的函数参数等。scala的嵌套和作用域思想是什么???为何嵌套和作用域的思想对于函数是一等公民的语言中及其重要???
3)一等函数:函数字面量与函数值
什么是函数字面量?基本类型绝大部分都可以通过字面量来表示,比如 4,12 表示一个int值 2.3 表示double值等,函数也可以通过这种字面量来表示一个值(函数值)。而def形式是在定义一个函数,并不是一个值。再例如 val a:Int = 3定义Int类型的a,5是Int类型的值。
一等函数,不仅可以通过定义和调用使用它们,还可以写成函数字面量,当作值来传递。
函数字面量被编译进类,并在运行时形成对象(及函数值)。
函数字面量和函数值的区别:源码与运行时。如同蓝图与建筑,类与对象。(这里函数字面量就是类,函数值就是对象)
函数值既是对象又是函数
val increase = (x: Int) => x + 1 //像对象一样传值给变量 increase(10) //像函数一样调用参数
函数字面量中的方法部分可以用{}
val increase = (x: Int) => { val y = x + 1 y +1 }
既然函数作为值,就可以传递给函数,形成高阶函数。
栗子:集合类中的两个方法foreach与filter同是以函数作为参数的函数。
目标类型化 缩减函数字面量的无用字符??
占位符 _ 一个占位符只能表示一个函数参数,且这个函数参数在函数体中只能被用到一次。
栗子:(x:Int) => x+1 写成 _ + 1
(x:Int,y:Int) => x+y 写成 (_:Int) + (_:Int) 不简洁,但是如果是某一函数调用两个参数的函数时,可以通过类型推断(?)直接写成xx.yy(_ + _)
部分应用函数
1.通过部分应用函数将def转换为函数值
def sum(a:Int,b:Int,c:Int) = a+b+c val temp_sum = sum _
2.当指定了一些函数参数时候 部分应用函数的占位符还需要添加类型
scala> val mysum = sum(1,_,_) <console>:11: error: missing parameter type for expanded function ((x$1, x$2) => sum(1, x$1, x$2)) val mysum = sum(1,_,_) ^ <console>:11: error: missing parameter type for expanded function ((x$1: <error>, x$2) => sum(1, x$1, x$2)) val mysum = sum(1,_,_) ^ scala> val mysum = sum(1,_:Int,_:Int) mysum: (Int, Int) => Int = <function2>
3. 1和2的原理是编译器将函数编译成类,mysum(2,3)其实在调用mysum.apply(2,3)。 apply方法?
闭包
自由变量与绑定变量
闭包是函数值。通过自由变量创建的函数值称为闭包。
闭包捕获自由变量而不是变量所指向的值。所以变量的改变会影响闭包,同样闭包的也能改变自由变量。
scala> var s = 10 s: Int = 10 scala> val mydef = (a:Int) => a + s mydef: Int => Int = <function1> scala> mydef(1) res0: Int = 11 scala> s = 12 s: Int = 12 scala> mydef(1) res1: Int = 13
scala> val sumList = List(1,2,-1,-2,4) sumList: List[Int] = List(1, 2, -1, -2, 4) scala> var sum = 0 sum: Int = 0 scala> sumList.foreach(sum += _) scala> sum res3: Int = 4
个人理解 当闭包的自由变量是函数参数时。每一次函数传参都会形成新的闭包。由于函数参数是val 所以函数体内形成的闭包的自由变量恒定。
重复参数
定义重复参数只要在类型后面加*
scala> def sum(a:Int*) = a.sum sum: (a: Int*)Int scala> sum(1) res4: Int = 1 scala> sum(1,3,4) res5: Int = 8
形成的类型其实是数组。所以可以调用数组sum方法。
然而不能传数组给sum函数,需要将数组转为arr: _*形式(这种形式是告诉编译器把数组中的每一个参数传给函数)
scala> val arr = Array(1,2,3,4,5,6,7,8,9) arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9) scala> sum(arr) <console>:13: error: type mismatch; found : Array[Int] required: Int sum(arr) ^ scala> sum(arr: _*) res7: Int = 45
指定名字的调用参数
scala> def speed(distance: Float, time: Float): Float = distance / time speed: (distance: Float, time: Float)Float
scala> speed(100, 10) res9: Float = 10.0 scala> speed(distance=100,time=10) res10: Float = 10.0 scala> speed(time=10,distance=100) res11: Float = 10.0
一般常用方式是默认顺序+指定参数 配合使用 (没记错的话,Spark中比较常见)
默认的参数值
scala> def printTime(out:java.io.PrintStream = Console.out) = out.println("time = " + System.currentTimeMillis()) printTime: (out: java.io.PrintStream)Unit scala> printTime() time = 1478591319201 scala> printTime(Console.err) time = 1478591327893
然而默认的参数值常常与指定参数配合使用
scala> def printTime2(out: java.io.PrintStream = Console.out, divisor: Int = 1) = out.println("time = " + System.currentTimeMillis()/divisor) printTime2: (out: java.io.PrintStream, divisor: Int)Unit scala> printTime2(out = Console.err) time = 1478591428641 scala> printTime2(divisor = 1000) time = 1478591435
尾递归:scala编译器对尾递归进行了特殊处理,使得与指令形式的while效率相当。但由于JVM的限制使得尾递归并不能完全支持(间接尾递归,函数值的尾递归),支持直接尾递归。
以上是关于函数与闭包的主要内容,如果未能解决你的问题,请参考以下文章