【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}")

 

【Scala】03 函数

上一篇:[题解]AtCoder Beginner Contest 209 E


下一篇:数据结构绪论