zoukankan      html  css  js  c++  java
  • 【Scala】03 函数

    1、Scala的方法语法:

    object Hello {
      def main(args : Array[String]) : Unit = {
    
        // scala 允许在方法的声明中再声明方法,并且调用
        def someFunction(x : Int, y : String): Unit = {
          println("this function has been called! (In this main method)")
        }
    
        // 调用方法中声明的
        someFunction(100, "str")
    
        // 调用这个对象声明的
        val returnVal = Hello.someFunction(2, "ol")
      }
    
      // 在对象中也可以声明方法,支持和方法中重名
      def someFunction(x : Int, y : String): Int = {
        println("this function has been called! (In this single instance)")
        return 100
      }
    }

    2、参数和返回值

        // 没有参数 没有返回值
        def method01(): Unit = {
          print("method01")
        }
        // 有参数 没有返回值
        def method02(x : Int, y : String): Unit = {
          print("method02")
        }
        // 有参数 有返回值
        def method03(x : Int, y : String): Int = {
          print("method01")
          return 100
        }
        // 没有参数 有返回值
        def method04(): Int = {
          return 300
        }

    3、参数特性

    // 在参数类型后面加上*号表示该参数是可变参数
    def method(str : String*): Unit = {
      println(str)
    }
    method("hello", "scala") // 打印结果是一个数组序列 ArraySeq(hello, scala)
    
    // 多个参数必须将可变参数放在最后一个
    def method2(x : Int, y : Double, string: String*): Unit = {
    
    }
    
    // 默认参数
    def method3(name : String = "这是默认值"): Unit = {
      println(name)
    }
    
    method3() // 这样无参也可以被调用,因为已经设定了一个默认值在这里 C++是支持这种操作的
    method3("jojo")
    
    // 带名参数,这个参数的意思是,你可以在调用的时候强制指定注入的参数是给哪个参数的
    def method4(name : String, age : Int, gender : Boolean): Unit = {
    
    }
    // 带名参数的意义就在于支持打乱顺序入参
    method4(gender = true, name = "张三", age = 30) 

    4、函数特性:

    // Lambda表达式特性
    
    // 1、可以省略return,Scala支持 函数体的最后一段代码最为返回的结果
    def f0(str : String) : String = {
      str // 这里就省略的了 return
    }
    println(f0("param"))
    
    // 2、函数体只有一段代码可以省略阔话表示
    def f1(str : String) : String = str
    
    // 3、如果类型可以推断出来,则不需要强制声明返回类型是什么
    def f2(str : String) = str
    
    // 4、写了Return就必须声明返回类型
    def f3(str : String) : String = return str
    
    // 5、明确了返回类型为Unit,则 return没有任何意义
    def f4(str : String) : Unit = return str // 这个str无意义
    
    // 6、Unit返回类型 可以直接向Java一样声明
    def f5(str : String) {
      println(str)
    }
    
    // 7、没有参数,但是声明的时候写了,调用可以没有括号,加括号也行
    def f6() {
      println("str")
    }
    f6() // 这样调用正常
    f6 // 这样调用也没问题
    
    // 8、如果声明的时候就没有参数列表,调用绝不能写括号
    def f7 {
      println("str")
    }
    f7 // 调用正常
    // f7() // 这样调用编译报错
    
    // 如果不关心名称,只需要逻辑处理,这里也可以匿名声明
    (name : String) => {
      println(name)
    }

    5、函数可以作为参数注入

        val funcRef = (string : String) => {
          println(string)
        }
    
        // 定义一个函数,参数可以是一个函数
        def funcForFunc(function: String => Unit): Unit = {
          function("asdas");
        }
    
        // 这里很奇怪的一点是,参数为什么可以在里面注入,而外面只需要声明入参数函数?
        funcForFunc(funcRef)
    
        // 我有点想明白了,这样的话,参数是顶死的,但是允许套用不同的方法,只要方法符合这个参数的方法类型要求即可被套用
    
        // 语法缩写
    
        // 1、参数类型可以省略不写,只要能够被推导出来
        funcForFunc((name) => {println(name)})
    
        // 2、参数只有一个可以不写参数括号
        funcForFunc(name => {println(name)})
    
        // 3、执行的语句只有一行可以不写花括号表示
        funcForFunc(name => println(name))
    
        // 4、参数只使用过一次的情况,可以不声明参数了,直接使用下划线表示
        funcForFunc(println(_))
    
        // 5、最后是调用推断
        funcForFunc(println)

    用法案例介绍:

        // 定义一个二元运算的函数,只操作固定的1和2个值,具体的运算由注入的函数实现
        def calcForThis(fun : (Int, Int) => Int) : Int = {
          fun(100, 200)
        }
    
        val sum = (a : Int, b : Int) => a + b
        val minus = (a : Int, b : Int) => a - b
        val max = (a : Int, b : Int) => if (a > b) a else b
        val min = (a : Int, b : Int) => if (a > b) b else a
    
    
        // 调用
        val s = calcForThis(sum)
    //    val s = calcForThis(minus)
    //    val s = calcForThis(max)
    //    val s = calcForThis(min)
    
        // 匿名函数简化
    
        // 原始完整调用
        println(calcForThis((a : Int, b : Int) => a + b))
        // 对匿名简化
        println(calcForThis((a, b) => a + b))
        // 参数简化
        println(calcForThis( _ + _))

    这和Java或者之前学习的编程不太一样

    我们创建各种方法,是为了对参数进行处理,方法是为了封装逻辑

    现在感觉是反过来,参数是既定的,封装的成方法了,这个感觉写起来像那个接口的感觉一样

    6、函数的进一步操作

        // 1、在函数体中声明函数,然后又调用函数
        def foo() {
          println("foo ...")
        }
        foo()
    
        // 2、作为值进行传递
        def foo2() : Int =  {
          100
        }
        val res = foo2()  // 将函数的返回值传递给 res
        val res2 = foo2() _ // 将函数自己作为值传递给 res2
        val res3:() => Int = foo2 // 如果明确变量类型? 可以不使用下划线将函数作为整体传递给参数
    
    
        // 3、函数入参 作为函数参数
        def subjectFunction(paramFunction : (Int, Int) => Int): Int = {
          paramFunction(100, 314) // subjectFunction 主体函数, paramFunction参数函数
        }
        subjectFunction(_ + _)
        subjectFunction(_ * _)
    
        // 4、函数作为返回值返回
        def f1() = { // 声明外层一个f1函数
          def f2() = { // 在内层中声明一个f2函数
    
          }
          f2 _ // 并且返回f2这个函数
        }
        val s = f1() // 调用就是把f2函数传递给s

    7、对集合的一些处理操作

        // 对数组进行操作,如何操作作为一个抽象来定义,处理完毕返回一个新的数组
        def arrOp(arr : Array[Int], op : Int => Int) : Array[Int] = {
          for (elem <- arr) yield op(elem)
        }
    
        // 声明一个加一操作
        def increaseOne(elem : Int) = {
          elem + 1
        }
    
        // 使用
        val sourceArr = Array(1, 10, 30, 100) // 声明一个原始的数组
        val newArr = arrOp(sourceArr, increaseOne) // 注意这里给方法的时候是把方法本身传递,而不是调用
        println(newArr.mkString(","))
    
        // 简化调用
        val newArr2 = arrOp(sourceArr, _ + 1) 

    8、案例练习:

        // 声明匿名函数给fun变量
        val fun = (a : Int, a2 : String, a3 : Char) => {
          if(a == 0 && a2 == "" && a3 == '0') false
          else true
        }
    
        // 调用
        println(fun(0, "", '0'))

    9、初见柯里化

        // 但是这段声明的语法看不太懂
        def func(i: Int): String => (Char => Boolean) = {
          def f1(s : String) : Char => Boolean = {
            def f2(c : Char) : Boolean = {
              if(i == 0 && s == "" && c == '0') false
              else true
            }
            f2
          }
          f1
        }
    
        // 简写
        def func2(i: Int): String => (Char => Boolean) = {
          (s : String)  => {
            (c : Char) => {
              if(i == 0 && s == "" && c == '0') false else true
            }
          }
        }
        // 最外层有声明参数类型,也可以被省略
        def func3(i: Int): String => (Char => Boolean) = {
          s => c => if(i == 0 && s == "" && c == '0') false else true
        }
        // 柯里化?
        def func4(i: Int)(s: String)(c : Char):Boolean = {
          if(i == 0 && s == "" && c == '0') false else true
        }
    
        // 这里肯定看不懂
        println(func(0)("")('0'))
    
        // 这样来拆解出来 是这样调用的,只是没有变量接受,直接匿名进行调用了
        val resultFunction1 = func(0)
        val resultFunction2 = resultFunction1("")
        val res = resultFunction2('0')
        println(res)

    10、案例:

        // 闭包 & 柯里化
    
        // 闭包概念 : 一个函数,访问了外部的局部变量的值,这个函数和所在的范围称为闭包
        // 意义在于外部函数知晓完毕出栈之后,需要保留部分的参数内容,给内部函数进行计算和调用
    
        // 案例 将固定加数作为另一个参数传入,但是是作为第一层参数传入
        def addByParam():Int => Int = {
          val a = 4
          def addByPa2(b : Int): Int = {
            a + b
          }
          addByPa2
        }
    
        def addByParam2(a:Int):Int => Int = {
          def addByPa2(b : Int): Int = {
            a + b
          }
          addByPa2
        }
    
        def addByParam3(a:Int):Int => Int = a + _
        // 柯里化 把一个参数列表的多个参数,变成多个参数列表
        val res = addByParam2(39)(42)
    
        def addCurrying(a : Int)(b : Int) = {
          a + b
        }
        addCurrying(39)(42)

    11、递归 Recursive

        /**
         * Scala的递归
         * 1、方法调用的是自身
         * 2、必须存在可以结束的逻辑
         * 3、参数应该有规律
         * 4、递归必须声明返回类型
         */
        // 阶乘案例
        def factorial(n : Int): Int = {
          if (n == 0) return 1
          factorial(n - 1) * n
        }
    
        // 尾递归
        def tailFact(n : Int)= {
          def loop(n : Int, currRes : Int) : Int = {
            if (n == 0) return currRes
            loop(n - 1, currRes * n)
          }
          loop(n, 1)
        }

    12、抽象控制

        // 控制抽象
    
        /**
         * 这里演示一段Java写法
         */
        val a = 100 // 声明一个常量
        def someFunction(s : Int) = { // 声明函数
          println(s)
        }
        someFunction(100) // 一般来说就是直接注入实际可见的值
        someFunction(a) // 或者是变量
    
        /**
         * Scala 希望不出现这些变量和字面值
         * 因为函数代表了一切内容
         */
        def returnTen() = 10
        someFunction(returnTen()) // 要以这种方法来进行入参的表达方式,就是控制抽象

    13、传名参数

        // 传名参数,强调的是参数是一段代码块,执行包括代码块中的内容
        def fun(a : => Int) = {
          println(s"a : ${a}")
          println(s"a : ${a}")
        }
    
        def f1() = {
          println("f1 called")
          12
        }
    
        // 使用
        fun(f1()) // f1被调用了两遍 a即表示函数,出现一次调用一次
    
        fun(23) // 为什么可以传递23?
    
        /**
         * 因为23就是一个函数的返回
         */
        () => 23

    14、实现自定义循环的案例

        // 实现自定义循环
        def customWhile(condition : Boolean):(=> Unit)=> Unit= {
          def doCustomLoop (operate : => Unit): Unit = {
            if (condition) {
              operate
              customWhile(condition) (operate)
            }
          }
          doCustomLoop _
        }
    
        // 柯里化表达
        def customWhile2(condition: => Boolean)(operate: => Unit): Unit = {
          if (condition) {
            operate
            customWhile2(condition)(operate)
          }
        }
    
        var n = 10
        customWhile2(n > 0) {
          println(n)
          n -= 1
        }

    15、懒加载处理

        val sum = (a : Int,b : Int) => {
          println("sum has been called")
          a + b
        }
        val minus = (a : Int,b : Int) => {
          println("minus has been called")
          a - b
        }
    
        val result1 : Int = minus(11, 31)
        // 惰性加载
        lazy val result2 : Int = sum(11, 31)
    
        println("- - - 函数应该被调用了 - - -")
        println(s"函数应该被调用了 ${result2} ${result1}")
  • 相关阅读:
    CSS实现小三角小技巧
    Javascript原型继承 __proto__
    99乘法表
    函数式编程之纯函数
    函数式编程 本质(笔记)转载
    函数式编程之柯里化(curry)
    Javascript-常用字符串数组操作
    第十章
    第九章
    第八章读后感
  • 原文地址:https://www.cnblogs.com/mindzone/p/14994827.html
Copyright © 2011-2022 走看看