叽叽喳喳,嘻嘻哈哈

导航

scala--函数和闭包

函数和闭包

方法

作为对象成员的函数叫方法。

本地函数

限制某些函数的被访问权限,可以通过:

​ 1、 private 在函数前添加private 关键字,使方法成为私有方法

​ 2、把函数定义到别的函数内部,就像定义本地变量一样。

class InBuild{
  private def myPirvateFun():Unit = println("private fun")
  def publicFun():Unit = println("public func")
  def callPrivate():Unit = { //一个公共方法,包裹调用私有方法
    (new InBuild).myPirvateFun()
  }
}
object InBuildControl{
  def main(args: Array[String]):Unit = {
    val inner = new InBuild
    inner.publicFun()
//    inner.myPirvateFun()  // 在外部调用 myPrivate 方法是不行的。但是如果,类为InBuildControl,意思是,它是该对象的伴生的伴生类,那么这个时候,是可以直接使用的。因为,伴生类,伴生对象,可以互相访问对面的私有成员。
    inner.callPrivate() // 调用对象的公共方法,进而调用对象的私有方法
  }
}


class InBuild{
  def publicFun():Unit = println("public func")
  def callPrivate():Unit = { // callPivate 彻底包裹了myPrivate函数,使其成为了本地函数,
    def myPirvaeFun():Unit = println("private fun")
    myPirvaeFun()
  }
}
object InBuildControl{
  def main(args: Array[String]):Unit = {
    val inner = new InBuild
    inner.publicFun()
//    inner.myPirvaeFun() // myPrivate函数为对象callPrivate方法的本地函数,不再是对象的方法了。
    inner.callPrivate()
  }
}

头等函数

​ scala 的函数是头等函数,你不仅可以定义和调用函数,还可以把他们写成匿名的字面量,并把它作为值传递。

建议 先读一下这篇文章

函数字面量是代码在源码期间的叫法,函数值是代码运行期间的叫法,我认为,可以把他们统一叫法,他们都是某个函数类型具体的值。就像String类型的某个具体值是“hello” 一样。

如有个函数类型如下:

Int=>Int //参数是一个Int类型,返回一个Int类型的值

那么它可以有如下具体的表现

val addOne:Int=>Int = {x => x+1} //一个函数变量叫addOne 它是Int=>Int类型。具体的函数体是 x=>x+1
val addTwo:Int=>Int = x => x+2 // => 表示,你可以把任何的x 映射成x+2 。函数值是对象,可以将其存入变量。调用的时候:
// addOne(10)  得到 12
val addThree = (x:Int) => x+3
val addFour = (x:Int)=> (x+4):Int

任何函数值都是某个扩展了scala包的若干FunctionN 特质之一的类的实例。如Function0是没有参数值的函数。Function1是只有一个参数的函数值。每个FunctionN特质都有一个apply方法来调用函数。

如果想让字面量包含多条语句,那么可以使用{}包裹函数体

 val addPrient = (x:Int) =>{
    println("hello")
    println("world")
    x+10
  }

函数字面量也可以做匿名函数来使用

val aList = List("one","two","three")
aList foreach((x:String)  => print(x)) //这里使用了操作符标注的方法,即像 1+2 中的+一样使用foreach方法,它其实是 aList.foreach()
  
println(aList filter((x:String) => x.length() > 3))
  //List(three) filter 接受一个过滤条件,返回过滤条件为真的元素
//上面的foreach,filter 都是由aList这个字符串列表来调用,所以,foreach和filter 里面的函数,知道x 具体的类型是什么,所以,两个x的类型都可以省略不写
aList.foreach((x)  => print(x.length))
println(aList filter((x) => x.length > 3))
占位符

如果想让函数字面量更简洁,可以用_ 当作 一个多个 参数的占位符。只要每个参数在函数字面量内只出现一次。

println(aList filter(_.length > 3)) //把 _ 当作一个参数的占位符
  //val f = _ + _ //这种方式,函数字面量无法推断两个变量的类型,所以是会报错
  val f = (_:Int) +(_:Int)   //这也是一个函数字面量。多个 _ 指代依次多个参数,而不是多个_ 重复指代一个参数。
  println(f(2,20))
aList.foreach(println(_)) //这个是 _ 代表整个列表,运行实际是下面这个形式
  aList.foreach(println _)  //函数名和占位符之间有个空格,这样才能分开。
aList.foreach(x => println(x))
部分应用函数

scala中,当你调用函数,传入任何需要的参数,实际上是把函数应用到参数上。

   def sum(a:Int,b:Int,c:Int) = a + b + c
    println(sum(1,2,3))   //这就是把函数 sum 应用到参数  1 2 3 上

部分应用函数是一种表达式——你不需要提供函数需要的所有参数,而是提供部分或者完全不提供。

var a = def sum _  //这个代码会以部分应用表达式 sum_ ,实例化一个函数值。 该函数值带有 3 个 整数参数。
a(1,2,3) //它接受了三个参数,然后就回去调用sum() 函数求值。
val b = sum(1,_:Int,3) // 这是实例化一个缺少一个整数参数的函数值
println(b(10))

如果要写一个省略所有参数的函数表达式,如 print _ sum _,而且代码那个地方正需要一个表达式。 那么_可以省略。

aList.foreach(println)
闭包

有一个函数字面量含有一个开放的自由变量,通过捕获对自由变量的绑定.从而对函数字面量进行“关闭”行动,这个函数就是闭包。

    var more = 1  
    val a = (x:Int) =>x +more //在这个函数字面量中,x是绑定的,或者说通过传参来绑定的。但是more如果么有上面那个语句,那么他是没有固定值的,他是一个自由变量。现在有了 more= 1,那么就会将more绑定了,这就是闭包。
    println(a(1))
    var more = 1
    val a = (x:Int) =>{
      x +more
      more = more+10092 //闭包中对自由变量进行了修改,那么也会影响到外面的自由变量。
    }
    println(a(1))
    println(more)
  }
    var more = 1
    val a = (x:Int) =>{
      val z = x +more
      more = more+10092
      z
    }
    println(a(1))//2
    println(more)//1903  这条和下一条可以看到 more确实受到了影响。自由变量more的值发生了变化
    println(a(1)) //1904 闭包只会捕获当前活跃的自由变量的值
   
    more = 1111  // 自由变量more的值发生了变化
    val b = (x:Int) =>{
      x +more
    }
    println(b(1)) //1112 闭包只会捕获当前活跃的自由变量的值
    println(more) //1111
    println(a(3)) //1114  闭包只会捕获当前活跃的自由变量的值
多个参数
    def manyString(args:String*): Unit ={ //scala中,你可以指明最后一个参数是重复的。从而允许用户向函数传入可变长参数列表。可以在类型之后加一个* 实现。    感觉和python 的 *arg 差不多啊。
        args.foreach(println)
    }

    manyString("hello","world","nihao")
   //函数内部重复参数的类型,是声明类型的数组,如本例其实是 Array[String]。foreach 方法也说明了这一点。但是如果你直接传入一个数组对象是不允许的。可以如下做
    val aArray = Array("HELLO","WORLD","HELLO")
    def manyString(args:String*): Unit ={
        args.foreach(println)
    }
    manyString(aArray) //会报错
    manyString(aArray: _*) //这个标注的意思是把aArray 的每个元素当作参数传给函数,而不是当成一个整体来作为一个参数传给函数。这其实和上面的  arg:String* 原理一样。只是把参数名由 arg变成了aArray,把上面那个指定了是String 数组,变成了 不指定类型的 ,可以是任意类型的数组。
尾递归

函数的最后一个动作是调用自己,那么就是尾递归。

scala编译器检测到尾递归,就会用新值更新函数参数,然后把它替换成一个回头函数开头的跳转。从而取消运行期的开销。

posted on 2017-07-16 21:50  叽叽喳喳,嘻嘻哈哈  阅读(195)  评论(0编辑  收藏  举报