zoukankan      html  css  js  c++  java
  • 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编译器检测到尾递归,就会用新值更新函数参数,然后把它替换成一个回头函数开头的跳转。从而取消运行期的开销。

  • 相关阅读:
    8.10
    今日头条笔试题 1~n的每个数,按字典序排完序后,第m个数是什么?
    Gym 100500B Conference Room(最小表示法,哈希)
    CodeForces 438D The Child and Sequence(线段树)
    UVALIVE 6905 Two Yachts(最小费用最大流)
    Gym Conference Room (最小表示法,哈希)
    hdu 2389 Rain on your Parade(二分图HK算法)
    Codeforces Fox And Dinner(最大流)
    zoj 3367 Counterfeit Money(dp)
    ZOJ3370. Radio Waves(2-sat)
  • 原文地址:https://www.cnblogs.com/jijizhazha/p/7192043.html
Copyright © 2011-2022 走看看