1. 程式人生 > >Spark學習筆記3-Scala函數語言程式設計

Spark學習筆記3-Scala函數語言程式設計

1.函式

1.1函式的定義

scala中函式的定義如下程式碼:

scala> def fun1(name: String){println(name)}
fun1: (name: String)Unit

程式碼是定義了一個傳入為String型別的引數name,並將name打印出來的函式。
其中def fun1(name: String){println(name)}中,def為定義函式用的識別符號,fun1是函式名,name是函式引數,其型別為Sting型別,{ }內為函式的實現,println(name)表示列印引數name的值。因為沒有定義函式的返回值,所以返回為Unit型別。

該函式的使用,程式碼如下:

scala> fun1("Spark")
Spark

1.2函式賦值給變數

scala作為函數語言程式設計的語言。函式也是作為和變數一樣的一等公民。所以函式可以賦值給變數。程式碼如下:

scala> val fun1_v = fun1 _
fun1_v: String => Unit 

這裡程式碼實現的是把前面定義的函式fun1賦值給不可變變數fun1_v。其中要注意的是val fun1_v = fun1 中的 fun1 下劃線前面一定要有空格,這種寫法是用到了scala語法中強大的型別推斷。然後變數fun1_v就可以作為函式使用了。程式碼如下:

scala> fun1_v("Spark")
Spark

2.匿名函式

匿名函式:就是函式名可以匿名。也就是說在定義函式的時候可以不寫函式名。可以看下面的程式碼例子,如下:

scala> val fun2 = (content: String) => println(content)
fun2: String => Unit 

從程式碼中可以看出fun2是一個val的變數。後面是一個匿名函式的定義,這個匿名函式直接賦值給了fun2變數。其中content是匿名函式的引數,String是引數型別。=>後面是函式內容,表示列印傳入引數content。
下面是對這個匿名函式的使用,程式碼如下:

scala> fun2("Hdaoop")
Hdaoop

3.高階函式

3.1函式的引數也可以是函式

3.1.1例子一
我們先定義一個匿名函式,並賦值給變數hiScala,如下:

scala> val hiScala = (content : String) => println(content)
hiScala: String => Unit 

再定義一個引數為函式的函式,其中另一個引數為content。如下:

scala> def bigData(func: (String) => Unit,content: String) {func(content)}
bigData: (func: String => Unit, content: String)Unit

使用這個帶引數為函式的函式,如下:

scala> bigData(hiScala,"Spark")
Spark

上面的第二段程式碼引數內的func: (String) => Unit 就是定義了一個函式作為引數,函式名為func,func函式帶一個引數,這個引數為String型別(這裡注意:這個引數為寫明引數名稱,只定義了引數型別。因為在scala中定義函式時候若只有一個引數,引數名是可以省略的)。函式的返回值為unit。

上面的第三段程式碼是呼叫這個函式bigData,傳入的第一個引數為第一段程式碼定義的函式hiScala,第二個引數是一個字串“Spark”。

3.1.2例子二

scala中的map也是高階函式,引數也可以是函式。例子如下:
先定義一個數組。

scala> val array = Array(1,2,3,4,5,6,7,8,9)
array: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)

使用map函式,其中()裡的是用一個函式作為引數,item是這個函式的傳入引數,=>是函式的實現,對引數item擴大2倍,這裡初看與例子一不同,改成array.map((item) => 2 * item),這樣就可以和前面一樣了,這裡另加說明在scala中定義函式時候若只有一個引數,引數名是可以省略的,而且可以省略這個括號。

scala> array.map(item => 2 * item)
res5: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18)

用map來遍歷列印陣列值,程式碼如下:

scala> array.map(item => println(item))
1
2
3
4
5
6
7
8
9
res7: Array[Unit] = Array((), (), (), (), (), (), (), (), ())

3.2函式的返回值是個函式

先定義一個函式的返回值是個函式,如下:

scala> def func_Returned(content : String) = (message: String) =>println(message)
func_Returned: (content: String)String => Unit

使用這個函式func_Returned

scala> func_Returned("Spark")
res8: String => Unit 

從結果中看出,輸入時String,函式返回值unit,是一個函式function

為了更加方面看出呼叫過程,我們把content和message都打印出來。

scala> def func_Returned(content: String) = (message: String) => println(content + "   "+message)
func_Returned: (content: String)String => Unit

重新呼叫,並賦值給變數returned

scala> val returned = func_Returned("Spark")
returned: String => Unit 

呼叫returned

scala> returned("Scala")
Spark   Scala

4.高階函式的型別推斷

下面舉例說明,如下:

4.1例子一

定義一個函式spark。

scala> def spark(func: (String) => Unit, name: String){func(name)}
spark: (func: String => Unit, name: String)Unit

呼叫函式spark

scala> spark((name: String) => println(name),"Scala")
Scala

當只有一個引數的時候可以省略返回值。

scala> spark((name) => println(name), "Scala")
Scala

也可以省略括號

scala> spark(name => println(name), "Scala")
Scala

當引數只被使用一次時候,還可以用_表示這個引數

scala> spark(println(_), "Scala")
Scala

還可以連下劃線都木有

scala> spark(println, "Scala")
Scala

4.2例子二

scala> val array=Array(1,2,3,4,5,6,7,8,9)
array: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> array.map(2*_)
res0: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18)


scala> array.map(2*_).foreach(println _)
2
4
6
8
10
12
14
16
18

scala> array.map(2*_).foreach(println)
2
4
6
8
10
12
14
16
18

scala> array.map(2*_).filter(_ > 10)foreach(println)
12
14
16
18

5.閉包

定義:函式的變數超出其有效作用域的時候,我們還能夠對函式內部的變數進行訪問。

scala> def scala(content : String) =  (message : String) => println(content +" : "+message)
scala: (content: String)String => Unit
scala> val funcResult = scala("Spark")
funcResult: String => Unit 
scala> funcResult("Flink")
Spark : Flink

從第二段程式碼看出傳入引數是“Spark”給了content,這個content應該在第三段程式碼中超出作用域,所以
原理,scala為當前的函式生成了一個物件來維護這個變數,變數作為了這個類的成員變數,所以能一直被訪問。

6.柯里化(curring)

柯里化(curring)是如果一個函式有多個引數,可以轉換為多個函式,每個引數對於一個引數。例如一個函式帶有二個引數,柯里化後可以轉換為二個函式。見下面例子:

6.1 先舉一個普通引數求和的函式例子

程式碼如下:

scala> def sum(x: Int,y: Int) = x+y
sum: (x: Int, y: Int)Int
scala> sum(1,2)
res0: Int = 3

用柯里化來實現引數和,定義兩個函式,如下:

scala> def sum_Curring(x : Int) = (y: Int) => x+y
sum_Curring: (x: Int)Int => Int

下面用柯里化來呼叫,如下,一次呼叫二個函式。

scala> sum_Curring(1)(2)
res1: Int = 3

6.2 下面再看個更好的方式

定義如下

scala> def sum_Curring_Better(x : Int) = (y: Int) => x+y
sum_Curring_Better: (x: Int)Int => Int

定義還可以簡化成這樣,程式碼如下:

scala> def sum_Curring_Better(x : Int)(y: Int) = x+y
sum_Curring_Better: (x: Int)(y: Int)Int

呼叫過程如下:

scala> sum_Curring_Better(1)(3)
res2: Int = 4

7.高階函式reduceLeft()

直接看下面例子:

scala> (1 to 100).reduceLeft(_+_)
res0: Int = 5050

這個是求1到100求和的實現,reduceLeft(+)中的在剛開始時候,第一個下劃線是做第一個引數,第二個下劃線是做第二個引數,然後求和,所求的和再作為第一個引數,並對應第一個下劃線,這時候的第二個下劃線代表第三個引數。然後再用前面所求和來加上第三個引數,得到一個新的和再作為第一個引數,並對於第一個下劃線,這時候的第二個下劃線代表第四個引數。以此類推實現1到100求和。

8.集合的函數語言程式設計

8.1 List

程式碼如下:

scala> val list = List("Scala","Spark","Fink")
list: List[String] = List(Scala, Spark, Fink)

scala> list.map("The content is : " + _)
res0: List[String] = List(The content is : Scala, The content is : Spark, The content is : Fink)

scala> list.map(println)
Scala
Spark
Fink
res1: List[Unit] = List((), (), ())

8.2 flatMap

程式碼如下:

scala> val cal = list.map("The content is : " + _)
cal: List[String] = List(The content is : Scala, The content is : Spark, The content is : Fink)

scala> cal
res3: List[String] = List(The content is : Scala, The content is : Spark, The content is : Fink)

scala> cal.flatMap(_.split(" "))
res4: List[String] = List(The, content, is, :, Scala, The, content, is, :, Spark, The, content, is, :, Fink)

scala> cal.flatMap(_.split(" ")).foreach(print)
Thecontentis:ScalaThecontentis:SparkThecontentis:Fink

8.3 zip

程式碼如下:

scala> list.zip(List(10,6,5))
res7: List[(String, Int)] = List((Scala,10), (Spark,6), (Fink,5))

XianMing
轉載請註明出處