[scala-spark]4. 函數式編程

1. 幾個概念說明

  • 在Scala中,方法與函數幾乎可以等同(定義 使用 運行機制),只是函數的使用方法更加靈活多樣
  • 函數式編程是從編程方式的角度來談的。函數式編程把函數當成一等公民,充分利用函數、支持函數的多種使用方式(調用)。既可以作爲函數的參數使用,也可以將函數賦值給一個變量;此外函數的創建不用依賴類或者對象,而在Java中,函數的創建需要依賴類/抽象類或者接口
package com.lineshen.chapter5

object method2FunctionDemo {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    val res1 = dog.sum(10, 20)
    println(res1)

    //方法轉成函數
    val func = dog.sum _
    val res2 = func(20, 30)
    println(res2)

    //函數編程
    val func2 = (n1: Int, n2: Int) => n1 + n2
    val res3 = func2(30,40)
    println(res3)
  }
}

class Dog {
  def sum(n1: Int, n2: Int): Int = {
    n1 + n2
  }
}

>>30
>>50
>>70

函數的基本約定:

def 函數名([參數名:參數類型], ... ) [[:返回類型]] = {
  函數體
  return 返回值
}
  • 函數申明關鍵字是 def
  • [參數:參數類型] 表示函數的輸入,可以沒有,如果有多個,用逗號分割
  • 函數中的語句是爲了實現某一功能的代碼塊
  • 函數可以有返回值,也可以沒有;最好使用=,直接進行返回類型的推導
  • 如果沒有 reture ,默認以執行到最後一行的結果作爲返回值
package com.lineshen.chapter5

object FunDemo1 {
  def main(args: Array[String]): Unit = {
    val n1 = 1
    val n2 = 2

    println("sum="+getSum(n1,n2,"+"))
  }

  def getSum(n1: Int, n2: Int,opt: String) = {
    if (opt == "+"){
      n1 + n2
    } else if (opt == "-"){
      n1 - n2
    } else {
      null
    }
  }
}

本例中,n1+n2 / n1-n2 與 null的類型無法統一,就可以直接使用=進行返回類型的推導。

2. 函數遞歸

函數遞歸需要遵守的重要規則:

  • 程序執行函數時,就會創建一個新的受保護的獨立空間(新函數棧)
  • 函數的局部變量是相互獨立的,不會相互影響
  • 遞歸必須向逼近遞歸的條件逼近,否則就是無限遞歸

3. 函數的注意事項以及使用細節

  • 如果函數沒有形參,那麼函數調用過程中,不用寫()
  • 形參列表和返回值列表的數據類型可以是值類型,也可以是引用類型
package com.lineshen.chapter5

object funcDetails {
  def main(args: Array[String]): Unit = {
    val tiger0 = new tiger
    println(tiger0.name)
    val tiger1 = test(100, tiger0)
    println(tiger1.name)
  }

  def test(n1:Int, tiger0:tiger): tiger= {
    println("n1="+ n1)
    tiger0.name = "jack"
    tiger0
  }
}

class tiger{
  var name = "lineshen"
}

輸出結果: lineshen 100 jack

  • Scala中的函數可以根據函數體最後一行代碼自動推斷函數的返回值類型,該種情況下,return也可以不用寫
  • 因爲Scala可以自動推斷返回值類型,因此,直接使用=就好了,或者用[:Any=]
  • 一旦使用return,就必須指定返回值類型 [:返回值類型];如果不指定,就是表示該函數沒有任何返回值
  • 如果函數返回值制定了Unit,那麼return無效
  • Scala支持高級嵌套,如類中可以再一次聲明類;方法中可以繼續定義方法
package com.lineshen.chapter5

object dupFunc {
  def main(args: Array[String]): Unit = {
    def sayOK(): Unit = { // private final sayOK$1
      println("say ok")

      def sayOK(): Unit = { // private final sayOK$2
        println("say ok, say ok")
      }
    }
  }
}
  • 形參可以進行初始化,默認調用;實參會覆蓋形參;可以指定覆蓋特定的形式參數默認值【帶名參數】
  • Scala中的形參默認爲是val,函數體中不允許去修改
  • 遞歸無法進行類型自動推導,必須指定返回類型
  • 支持可變形參,用for進行解析

4. 惰性函數

惰性計算(儘可能延遲表達式求值)是許多函數式編程語言的特性。惰性集合在需要時提供其元素,無需預先計算它們,這帶來了一些好處。首先,可以將耗時的計算推遲到絕對需要的時候。其次,可以創造無限個集合,只要它們繼續收到請求,就會繼續提供元素。函數的惰性使用能夠得到更高效的代碼。Java並沒有爲惰性提供原生支持, Scala提供了,使用很方便。

object layDemo {
  def main(args: Array[String]): Unit = {
    lazy val res = sum(10, 20)
    println("-------------")
    println("-------------")
    println("-------------")

    println(res)
  }

  def sum(n1: Int, n2: Int): Int = {
    n1 + n2
  }

輸出結果(lazy函數只能用val進行修飾,爲了保證線程安全):

-------------
-------------
-------------
30

5. 異常處理

  • 拋出異常

Scala 拋出異常的方法和 Java一樣,使用 throw 方法,例如,拋出一個新的參數異常:

throw new IllegalArgumentException
  • 捕捉異常

異常捕捉的機制與其他語言中一樣,如果有異常發生,catch字句是按次序捕捉的。因此,在catch字句中,越具體的異常越要靠前,越普遍的異常越靠後。 如果拋出的異常不在catch字句中,該異常則無法處理,會被升級到調用者處。

捕捉異常的catch子句,語法與其他語言中不太一樣(java使用了try-catch-catch-finnaly 無論有沒有異常 finally代碼塊都會執行,一般用來釋放資源)。在Scala裏,借用了模式匹配的思想來做異常的匹配,因此,在catch的代碼裏,是一系列case字句,如下例所示:

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException

object exceptionDemo {
  def main(args: Array[String]) {
    try {
      val f = new FileReader("input.txt")
    } catch {
      case ex: FileNotFoundException => {
        println("Missing file exception")
      }
      case ex: IOException => {
        println("IO Exception")
      }
    }finally{
      println("Programming has run")
    }
  }
}

輸出結果:

Missing file exception
Programming has run   【實際應用中,一般用於釋放資源】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章