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

  • 相关阅读:
    android用户界面对话框
    JSP+JAVABEAN+SERVLET模式的注册实例实现
    android用户界面组件Widget网络视图WebView
    android广播事件处理broadcast receive
    android用户界面组件Widget地图视图MapView
    android用户界面组件Widget画廊视图Gallery
    android用户界面组件Widget网格视图GridView
    Windows Phone 7 hello world
    Android组件的通讯Intent
    android的互联网开发
  • 原文地址:https://www.cnblogs.com/jijizhazha/p/7192043.html
Copyright © 2011-2022 走看看