函数与闭包

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的限制使得尾递归并不能完全支持(间接尾递归,函数值的尾递归),支持直接尾递归。

 

以上是关于函数与闭包的主要内容,如果未能解决你的问题,请参考以下文章

JS---闭包

Go 匿名函数与闭包的使用

闭包 与 装饰器

闭包函数与装饰器

javascript 匿名函数及闭包----转载

python闭包与装饰器