跳转至

Day04

Scala融合了面向对象编程及函数式编程。

  • 面向对象:对数据和行为的封装。
  • 函数式编程:函数可当做一个值进行传递,具备确定性,适用于高并发、分布式计算。

📌 函数和方法

  • 函数:1.完成某一功能的程序语句的集合,称为函数。2.没有重载和重写的概念。
  • 方法:1.类中的函数称之方法。2.可以进行重载和重写。

📌 至简原则

  • 如果函数体只有一行代码,可省略花括号
  • 如果返回值类型能推断出来,那么返回值类型可省略为=>
  • 如果参数按顺序只使用一次,可直接使用下划线代替
  • 如果参数列表中只有一个参数,小括号可省略
  • 如果没有参数列表,函数声明可以省略小括号,而调用时必须省略小括号
  • 如果期望是无返回值类型,可以省略等号
  • return可省略,以最后一行作为返回值
  • 如果有return,则必须指定返回值类型
  • 如果不关心名称,只关心逻辑处理,那么函数名def可以省略,即匿名函数lambda表达式
object Learn01_FuncSimpify {
  def main(args: Array[String]): Unit = {
    // 参数默认值,一般放在参数列表后面
    // 可变参数,一般放在参数列表最后面
    // 两者不能混用
    def func1(str1: String = "", str2: String) {
      println("str1:" + str1)
      println("str2:" + str2)
    }

    def func2(str_collection: String*): Seq[String] = str_collection

    func1(str2 = "whm")
    val seq = func2("1", "2") // WrappedArray(1, 2)

    // 使用匿名函数:reduceByKey((x + y) => { x + y }) // reduceByKey(),Spark的方法,实现分组+聚合
    // 根据至简原则可简化为:reduceByKey(_ + _)
  }
}

📌 高阶函数的使用

  • 函数作为值进行传递
  • 函数作为参数进行传递
  • 函数作为返回值进行传递
object Learn02_HighOrderFunc {
  def main(args: Array[String]): Unit = {
    def func(n: Int) = n + 1

    // 1. 函数作为值进行传递
    val f1 = func _
    // 等价于
    val f2: Int => Int = func

    println(f1) // 此时打印func函数的引用
    println(f1(1))


    // 2. 函数作为参数进行传递
    def calc(function: (Int, Int) => Int, i: Int, i1: Int) = {
      function(i, i1)
    }

    //println(calc((a, b) => a + b, 1, 2))
    println(calc(_ + _, 1, 2)) // 上一行的匿名函数进行简化

    // 3. 函数作为返回值进行传递
    def func1(): Int => (String => Boolean) = {
      def func2(n1: Int): String => Boolean = { // 好处是可以使用外部函数的变量,这种情况称之为闭包
        def func3(n2: String): Boolean = {
          if (n1.toString == n2 && n2 != "") true else false
        }
        func3
      }
      func2
    }

    println(func1()(1)("1"))
    println(func1()(1)("2"))

    // 使用匿名函数简化func1函数体
    def simpify_func1(): Int => (String => Boolean) = {
      // 1.使用匿名函数,先省略掉方法名
      // 2.单行代码可省略返回值、花括号
      // 3.函数返回类型可推导,省略为`=>`
      // 4.参数列表已定义了嵌套函数的返回类型,所以匿名函数的参数类型可省略
      n1 => n2 => if (n1.toString == n2 && n2 != "") true else false
    }

    println(simpify_func1()(1)("1"))
    println(simpify_func1()(1)("2"))

    // 使用柯里化简化func1函数体
    def currying_func1()(n1: Int)(n2: String): Boolean = {
      if (n1.toString == n2 && n2 != "") true else false
    }

    println(currying_func1()(1)("1"))
    println(currying_func1()(1)("2"))

  }
}
// 对数组进行处理,处理过程以函数作为传参,并处理后的结果作为返回值(有点像策略模式?)
def arrOperation(arr: Array[Int], function: Int => Int): Array[AnyVal] = {
  for (i <- arr) yield function(i)
}

val arr: Array[Int] = Array(1, 2, 3, 4, 5)
// Array是引用类型,不能直接打印
println(arrOperation(arr, _ * 2).mkString(","))
println(arrOperation(arr, (i: Int) => if (i % 2 == 0) i else 0).mkString(",")) // todo,如果要把0的从数组中删除,应该怎么处理?

补充

  • 在函数式编程中,尽量少用多参数函数。
  • 通过函数柯里化,将接受多个参数的函数,转换成一系列接收一个参数的函数。
  • 函数调用时使用了外部函数的变量,这种情况称之为闭包;柯里化的底层一定是闭包。

📌 递归

  • 要有跳出条件
  • 递归层级越多,可能会损耗更多栈空间资源
  • 支持@tailrec尾递归:在最后一行调用自身而不进行额外计算,及时释放栈空间
object Learn03_Tailrec {
  def main(args: Array[String]): Unit = {
    /* 递归
    * 要有跳出条件
    * 递归层级越多,可能会损耗更多栈空间资源
    * 支持@tailrec尾递归:在最后一行调用自身而不进行额外计算,及时释放栈空间
    */
    // 尾递归实现阶乘
    def factorial(n: Int): Int = {
      @tailrec
      def fact(n: Int, acc: Int): Int = {
        if (n <= 1) return acc
        fact(n - 1, n * acc)
      }
      fact(n, 1)
    }
    println(factorial(5))
    println(factorial(1))
  }
}