scala(一)方法&函數

寫在前面

衆所周知,scala一貫宣稱本身是面向函數的編程,(java表示不服,我是面向bean的編程!)那什麼是函數?java

在接觸java的時候,有時候用函數來稱呼某個method(實在找不出詞了),有時候用方法來稱呼某個method,雖然method的中文翻譯就是「方法」,但對於java來講,方法和函數是等價的,或者說沒有函數這個概念。編程

而對於scala,這二者彷佛有一個較爲明確的邊界。閉包

你會發現滿世界的函數,而你卻在寫方法app

 

Scala 方法&函數

方法

Scala的方法和java能夠當作是同樣的,只是多了點語法糖。ide

好比無參方法在申明時能夠不加括號,甚至在調用過程也不用加括號函數

def f = 1+1
println(f)

好比方法能夠添加泛型規則,這在java中只能在類申明ui

def f[T](t: T) = {t}

還有其它不少細節語法,遇到才深刻吧spa

通常而言只要知道函數的結構就行(可是我想說,spark的代碼就沒有一個函數長成這樣的啊..),請忽略下圖的「函數」字樣,其實就是方法scala

 

 

方法應用

def method(): Unit ={
  //本地方法
  def print(str:String): Unit ={
    println(str)
  }
  print("hello")
}

方法的語法仍是跟java差很少的,只是有些能夠省略而已。翻譯

比較重要的就是本地方法,即方法中嵌套方法

 

函數

Scala的函數是基於Function家族,0-22,一共23個Function Trait能夠被使用,數字表明瞭Funtcion的入參個數

 

函數語法

下面這四個函數的意義是同樣的

// println(fun1)
// println(fun2)
// println(fun3)
// println(fun4)
// 都爲<function2>
val fun1 = new Function2[Int,Int,Int]() {
  override def apply(v1: Int, v2: Int): Int = {
    v1+v2
  }
}

val fun2 = new ((Int, Int) => Int)() {
  override def apply(v1: Int, v2: Int): Int = {
    v1+v2
  }
}

val fun3 = (v1:Int,v2:Int) => v1+v2

// _能夠把method轉換成function
val fun4 = fun4Method _
def fun4Method(v1:Int,v2:Int): Int = {
  v1+v2
}

通常咱們都採用第三種fun3定義方式,也是最難懂的一個定義方式。具體結構參考下圖

 

 

那函數有什麼用呢?

Java裏只有方法都能適應一切需求,那scala又提出函數的概念確定有意義。

1.函數能夠直接賦值給變量,可讓函數很方便的傳遞

2.閉包(closure),能夠把靈活操做代碼塊,從而引伸出其餘靈活的語法

 

函數應用

在spark中,有不少方法入參中使用函數的場景,好比以下函數

defrunJob[T,U](fun: Iterator[T] => U ,resHandler: (Int, U) => Unit): Unit ={
  //忽略裏面的邏輯
}

其中的fun和resHandler都是函數

Fun是入參爲Iterator[T],返回值爲U的函數,一個入參的函數其實就是Function1的實例

resHandler是入參爲Int和 U無返回值的函數,二個入參的函數其實就是Function2

模擬spark中常見的一段代碼語法,拿一個普通scala類型的例子來講

//模擬spark的runJob方法
def runJob[T,U](fun: Iterator[T] => U ,resHandler: (Int, U) => Unit): Unit ={
  val listBuffer = new ListBuffer[T]
  listBuffer.append("h".asInstanceOf[T])
  listBuffer.append("e".asInstanceOf[T])
  listBuffer.append("l".asInstanceOf[T])
  listBuffer.append("l".asInstanceOf[T])
  listBuffer.append("o".asInstanceOf[T])
  //這裏調用函數其實用到了伴生對象的概念,fun(xxx)就是fun.apply(xxx)
  val res = fun(listBuffer.iterator)
  //spark中,這裏是每一個partition的數據都存入arr,這裏作模擬就一個partition了:)
  resHandler(0,res)
}
 
//模擬調用runJob的方法
def main(args: Array[String]): Unit = {
  val arr = new Array[String](1)
  //fun函數的實際邏輯
  val fun = (it:Iterator[String]) => {
    val sb = new StringBuilder()
    while (it.hasNext)
      sb.append(it.next())
    sb.toString()
  }

  //resHandler函數的實際邏輯
  val resHandler = (i:Int,res:String) => arr(i) = res
  runJob[String,String](fun ,resHandler)
  println(arr.mkString(""))
}

其實就是傳遞函數的邏輯,和java的匿名類差很少(只有一個方法的匿名類),只是多了點語法糖

這麼作的好處也是不言而喻的

1.能夠構造出更抽象的方法,使得代碼結構更簡潔

2.spark的思想就是lazy,而函數傳遞也是一個lazy的過程,只有在實際觸發纔會執行

 

偏函數

英文爲PartialFunction,不知道這麼翻譯對不對,貌似都這麼叫。

PartialFunction實際上是Funtion1的子類

參考源碼

trait PartialFunction[-A, +B] extends (A => B)

A => B就是標準的函數結構

 

那PartialFunction有什麼做用呢?

模式匹配!

PartialFunction最重要的兩個方法,一個是實際的操做邏輯,一個是校驗,其實就是用來作模式匹配的。

 

參考資料

《Scala編程》

相關文章
相關標籤/搜索