1. 程式人生 > >scala的函數語言程式設計(二)

scala的函數語言程式設計(二)

 

引數型別推導

scala會盡可能地推匯出函式的型別,使得scala程式碼非常簡潔

//引數型別推斷
Array(1,2,3,4).map((x : Int) => x * 3)
//map方法會推斷出要出入一個 Int => Int 型別的函式,因此可以省略函式引數的型別,寫成:
Array(1,2,3,4).map((x) => x * 3)
//當只有一個引數時,可以省略引數的(),寫成:
Array(1,2,3,4).map(x => x * 3)
//當只有一個引數時,且在函式體中(也就是=>右邊)引數只出現一次,可以用“_”代替引數,寫成:
Array(1,2,3,4).map(_ * 3)

這種情況必須是在型別已知的情況下才能使用,也就是說引數型別的推斷,必須要有推斷依據,正如上面的例子Array的元素,也就是“1,2,3,4”作為函式引數x型別推斷的依據。像下面這樣沒有任何依據的推斷,會讓scala懵逼、報錯


scala> val a = _ * 3
<console>:11: error: missing parameter type for expanded function ((x$1: <error>
) => x$1.$times(3))
       val a = _ * 3
               ^
//正確的表達形式如下:

scala> val b = (_ : Int) * 3
b: Int => Int = $$Lambda$1112/
[email protected]
scala> val c : (Int) => Int = _ * 3 //這裡需要注意":",不推薦使用 c: Int => Int = $$Lambda$1115/[email protected]

一些常用的高階函式

學習了高階函式,我們就要使用它們,在這裡我再次強調無論是Spark中存在著大量的高階函式,不把這高階函式弄懂,學習Spark原始碼是很難很難的。

接下來介紹的一些用法,在使用Spark的Transformation和Action是很常見的,同時也感受一下,函式是變成的魅力吧。

map: 對傳入的每個元素都進行對映,返回一個處理後的元素
foreach: 對傳入的每個元素都進行處理,但是沒有返回值

scala> Array(1,2,3,4).map(_ * 3)
res9: Array[Int] = Array(3, 6, 9, 12)

scala> Array(1,2,3,4).map(_ * 3).foreach(println _)
3
6
9
12

filter: 對傳入的每個元素都進行條件判斷,如果對元素返回true,則保留該元素,否則過濾掉該元素

scala> (1 to 20).toArray.filter(_ % 2 == 0).foreach(println)
2
4
6
8
10
12
14
16
18
20

reduceLeft:接收一個二元引數的函式(兩個引數),並將它對應到集合中從左到右的所有元素。

scala> (1 to 20).reduceLeft(_ + _)
res13: Int = 210

sortWith: 也是接收一個二元引數的函式,對元素進行兩兩相比,進行排序

//升序
scala> Array(6,3,2,8,5,3,6,6,7).sortWith(_ < _)
res14: Array[Int] = Array(2, 3, 3, 5, 6, 6, 6, 7, 8)
//降序
scala> Array(6,3,2,8,5,3,6,6,7).sortWith(_ > _)
res15: Array[Int] = Array(8, 7, 6, 6, 6, 5, 3, 3, 2)

柯里化

柯里化(Currying)指的是將原來接收兩個引數的函式變成新的接收一個引數的函式的過程。新的函式返回一個以原有第二個引數作為引數的函式。原先呼叫兩個引數的函式變成了呼叫兩次一個引數的函式。下面舉一個例子就一目瞭然了,還用上一節scala的函數語言程式設計(一)的例子來看一下。


scala> def greetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
greetingFunc: (msg: String)String => Unit

//普通的呼叫方式
scala> val a = greetingFunc("hello")
a: String => Unit = $$Lambda$1254/[email protected]

scala> a("liumingxin")
hello, liumingxin

//柯里化
scala> greetingFunc("hello")("liumingxin")
hello, liumingxin

特殊處理(控制抽象)

如果我們定義一個方法,這個方法的引數是一個無參且返回Unit物件的函式,就像下面這樣:

scala> def greetingFunc(msg : () => Unit) = {println("hello,sid");msg()}
greetingFunc: (msg: () => Unit)Unit

呼叫這個方法,是這樣的:

scala> greetingFunc(() => println("liumingxin"))
hello,sid
liumingxin

在方法的呼叫過程中,需要再()中定義一個抽象函式,這樣寫醜吧吧唧的,可讀性太差。

要想在呼叫的過程中省掉"()=>",可以使用換名呼叫表示法:在引數宣告和呼叫該函式的地方略去"()",但宣告時保留"=>"

scala> def greetingFunc(msg : => Unit) = {println("hello,sid");msg}
greetingFunc: (msg: => Unit)Unit

scala> greetingFunc(println("liumingxin"))
hello,sid
liumingxin

這裡有一個小細節需要注意,也是我再編寫上面程式碼時遇到的一點小問題,我寫兩個方法咱們對比一下:

少了個括號,兩者的結果卻截然不同。 這裡需要複習和強調一下,

強調的是:在不適用換名呼叫表示法的時候,“函式”和“函式()”是有本質的區別

複習的是:方法體或函式體的最後一行程式碼代表方法或函式的返回值