scala(一)方法&函數


寫在前面

眾所周知,scala一向宣稱自己是面向函數的編程,(java表示不服,我是面向bean的編程!)那什么是函數?

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

而對於scala,這兩者似乎有一個較為明確的邊界。

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

 

Scala 方法&函數

方法

Scala的方法和java可以看成是一樣的,只是多了點語法糖。

比如無參方法在申明時可以不加括號,甚至在調用過程也不用加括號

def f = 1+1
println(f)

比如方法可以添加泛型規則,這在java中只能在類申明

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

還有其它很多細節語法,遇到才深入吧

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

 

 

方法應用

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編程》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM