1. 程式人生 > >Scala筆記整理(五):函數式編程

Scala筆記整理(五):函數式編程

大數據 Scala

[TOC]


作為值傳遞的函數

測試代碼如下:

package cn.xpleaf.bigdata.p4.function

/**
  * scala中關於函數的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
    functionOps1
  }

  /**
    * 作為值傳遞的函數
    * 將一個函數作為值傳遞給另外一個函數變量的時候,約定需要在函數後面加上:空格和下劃線
    * 相當於數據庫中的別名,或者數據庫表對應的視圖
    */
  def functionOps1: Unit = {
    def sayHi(name:String) = println("Hello, " + name)

    def sayHello = sayHi _

    sayHello("xpleaf")
  }
}

輸出結果如下:

Hello, xpleaf

匿名函數

測試代碼如下:

package cn.xpleaf.bigdata.p4.function

/**
  * scala中關於函數的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
    functionOps2
  }

  /**
    * 匿名函數,說白了就是沒有函數名字
    *     匿名函數就和java中的匿名內部類一樣,是只適合使用一次的函數
    *     一般如果一個函數的參數是一個函數,這種情況下多用匿名函數來作為參數進行傳遞
    */
  def functionOps2: Unit = {
    val printName = (name:String) => println("你好," + name)
    printName("xpleaf")
  }

}

輸出結果如下:

你好,xpleaf

其實前面在學習ArrayBuffer的時候已經有使用過匿名函數:

scala> val ab = ArrayBuffer[Int](3, 8, 2, 20, 5, 7)
ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 8, 2, 20, 5, 7)
scala> ab.sortWith((v1, v2) => v1 > v2)
res209: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(20, 8, 7, 5, 3, 2)

帶函數參數的函數(高階函數)

說明的作用有兩個(匿名函數作為參數和返回值為匿名函數),具體參考下面的測試代碼:

package cn.xpleaf.bigdata.p4.function

/**
  * scala中關於函數的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
//    functionOps1
//    functionOps2
    functionOps3
  }

  /**
    * scala的高階函數,就是函數的 [參數是函數] 的函數,把這種函數稱為高階函數
    */
  def functionOps3: Unit = {

    // 1.匿名函數作為參數
    def highOrderFunc(name:String, func:(String) => Unit): Unit = {
      func(name)
    }

    highOrderFunc("xpleaf", (name:String) => println("Hello, " + name))

    // 2.將匿名函數作為返回值傳遞給另外一個函數
    def getGoodBayFunction(gMsg: String) = (gName: String) => println(gMsg + ", " + gName)

    val goodbayFunction = getGoodBayFunction("good bye")
    goodbayFunction("xpleaf")

  }
}

輸出結果如下:

Hello, xpleaf
good bye, xpleaf

參數(類型)推斷

測試代碼如下:

package cn.xpleaf.bigdata.p4.function

import scala.collection.mutable.ArrayBuffer

/**
  * scala中關於函數的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
//    functionOps1
//    functionOps2
//    functionOps3
    functionOps4
  }

  /**
    * 對於匿名函數的省略寫法
    */
  def functionOps4: Unit = {
    val ab = ArrayBuffer[Int](1, 2, 3, 4, 5)

    // val newAB = ab.map((x:Int) => x * 100)
    // val newAB = ab.map((x) => x * 100)
    // val newAB = ab.map(x => x * 100)
    val newAB = ab.map(100 * _)
    println(newAB)  // ArrayBuffer(100, 200, 300, 400, 500)

  }
}

輸出結果如下:

ArrayBuffer(100, 200, 300, 400, 500)

常見高階函數——map函數

遍歷集合中的每一個元素,返回也是一個集合,集合大小和之前集合相等。
  • 快速產生0.1, 0.2, 0.3等方式的數字
scala> (1 to 9).map(0.1 * _).foreach(println(_))
0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9
  • 打印三角形
scala> (1 to 9).map("*" * _).foreach(println(_))
*
**
***
****
*****
******
*******
********
*********

在這裏,我們還用到了foreach,它和map很像,只不過它的函數並不返回任何值,foreach只是簡單的將函數應用到每個元素而已。

常見高階函數——filter函數

按照過濾條件,將原集合中不符合條件的數據過濾掉
  • 輸出所有匹配某個特定條件的元素,得到一個序列中的所有偶數
scala> (1 to 9).filter(line => line % 2 == 0).foreach(println(_))
2
4
6
8

scala> (1 to 9).filter(_ % 2 ==0).foreach(println)
2
4
6
8

常見高階函數——reduce函數

scala> (1 to 9).reduce((v1:Int, v2:Int) => v1 + v2)
res4: Int = 45
scala> (1 to 9).reduce(_ + _)
res6: Int = 45
scala> (1 to 9).reduceLeft(_ + _)
res7: Int = 45
scala> (1 to 9).reduceRight(_ + _)
res8: Int = 45

可以寫下面一個函數來驗證reduce函數的執行過程:

scala> val process = (v1:Int, v2:Int) => println(s"v1=${v1}, v2=${v2}")
process: (Int, Int) => Unit = <function2>
scala> def pro(v1:Int, v2:Int):Int = {
     |   println(s"v1=${v1}, v2=${v2}")
     |   v1 + v2
     | }
pro: (v1: Int, v2: Int)Int
  • reduce的執行流程
scala> (1 to 9).reduce((v1:Int, v2:Int) => pro(v1, v2))
v1=1, v2=2
v1=3, v2=3
v1=6, v2=4
v1=10, v2=5
v1=15, v2=6
v1=21, v2=7
v1=28, v2=8
v1=36, v2=9
res0: Int = 45
  • reductLeft的執行流程
scala> (1 to 9).reduceLeft((v1:Int, v2:Int) => pro(v1, v2))
v1=1, v2=2
v1=3, v2=3
v1=6, v2=4
v1=10, v2=5
v1=15, v2=6
v1=21, v2=7
v1=28, v2=8
v1=36, v2=9
res2: Int = 45
  • reductRight的執行流程
scala> (1 to 9).reduceRight((v1:Int, v2:Int) => pro(v1, v2))
v1=8, v2=9
v1=7, v2=17
v1=6, v2=24
v1=5, v2=30
v1=4, v2=35
v1=3, v2=39
v1=2, v2=42
v1=1, v2=44
res3: Int = 45

這樣的話執行過程就非常清晰了,reduce和reduceLeft都是從左邊的操作數開始,而reduceRight是從右邊的操作數開始。

常見高階函數——sortWith函數

scala> (1 to 9).sortWith((v1:Int, v2:Int) => v1 > v2).foreach(println(_))
9
8
7
6
5
4
3
2
1

scala> (1 to 9).sortWith(_ > _).foreach(println)
9
8
7
6
5
4
3
2
1

閉包

函數的變量不在其作用域內被調用,就是閉包的概念,看下面一個例子:

def closePackage: Unit ={
    def mulBy(factor:Double) = (x:Double) => factor * x
    val triple = mulBy(3)
    val half = mulBy(0.5)
    println(triple(14) +" " + half(14)) //42, 7
}

1)mulBy的首次調用將參數變量factor設為3,。該變量在(x:Double)=&gt;factor *x函數的函數體內被引用。該函數被存入triple.然後參數變量factor從運行時的棧上被彈出。

2)mulBy再次被調用,這次factor被設為0.5.該變量在(x:Double)=&gt;factor *x函數的函數體內被引用,該函數被存入half.

每一個函數被稱為閉包(closure)。閉包有代碼和代碼用到的任何非局部變量定義構成。

其實上面的mulBy函數就類似於下面這個:

def mulBy1(factor:Double, x:Double) = {
    factor * x
}

原本還希望進一步寫成下面這樣子的:

def mulBy2(factor:Double, x:Double) = {
    def work(x:Double) = {
        factor * x
    }
    return work
}

但在scala中不支持這樣的寫法,下面這樣寫才可以:

def mulBy2(factor:Double) = {
    (x:Double) => factor * x
}
// 加個return也不行

之前在Python中使用過閉包,類似上面的例子,Python就可以這樣寫:

def mulBy(factor):
    def work(x):
        return factor * x
    return work

測試如下:

>>> def mulBy(factor):
...     def work(x):
...             return factor * x
...     return work
...
>>> triple = mulBy(3)
>>> half = mulBy(0.5)
>>> triple(14)
42
>>> half(14)
7.0

當然,只是Python的語法格式就沒有scala那麽靈活了。

柯裏化(currying函數)

1、柯裏化(currying)指的是將原來接受2個參數的函數變成新的接受一個參數的函數的過程。新的函數返回一個以原有第二個參數作為參數的函數。

2、在函數調用的過程中,就變為了兩個函數連續調用的形式。在Spark源碼中,也有體現,所以對()()這種形式的Curring函數,一定要掌握。

以下函數接受一個參數,生成另一個接受單個參數的函數,要計算兩個數的乘積,調用如下:

/**
    * curryingFunction: 柯裏化函數
    */
def curryingFunction ={
    def totalSum(x:Int, y:Int) = println( x + y)

    //totalSum(4,5)

    def totalSumAtTime(x:Int) = (y:Int) => x + y
    println(totalSumAtTime(4)(5))
}

Scala筆記整理(五):函數式編程