“執(zhí)大象,天下往旬昭。
往而不害篙螟,安平太。
樂(lè)與餌问拘,過(guò)客止遍略。
道之出口,淡乎其無(wú)味骤坐,視之不足見(jiàn)墅冷,聽(tīng)之不足聞,用之不足既或油∧蓿”[1]
scala作為支持函數(shù)式編程的語(yǔ)言,scala函數(shù)式編程是scala的重中之重顶岸,spark當(dāng)中的計(jì)算都是用scala函數(shù)式編程來(lái)做腔彰,高級(jí)函數(shù)也是其獨(dú)特的一個(gè)特性,并且spark基于集合辖佣,這樣可以使scala發(fā)揮其對(duì)于集合計(jì)算的強(qiáng)大功能霹抛。首先,函數(shù)/變量同是一等公民卷谈,函數(shù)與變量同等地位杯拐,函數(shù)的定義可以單獨(dú)定義,可以不依賴于類、接口或者object端逼,而且獨(dú)立存在朗兵,獨(dú)立使用,并且可以賦值給變量顶滩。
函數(shù)傳名調(diào)用(Call-by-Name)余掖、傳值調(diào)用(Cal-by-Value)
Scala的解釋器在解析函數(shù)參數(shù)(function arguments)時(shí)有兩種方式:
- 傳值調(diào)用(call-by-value)
先計(jì)算參數(shù)表達(dá)式的值(reduce the arguments),再應(yīng)用到函數(shù)內(nèi)部礁鲁; - 傳名調(diào)用(call-by-name)
將未計(jì)算的參數(shù)表達(dá)式直接應(yīng)用到函數(shù)內(nèi)部盐欺。
package com.cgoshine.sh.demo
object Add {
def addByName(a: Int, b: => Int) = a + b
def addByValue(a: Int, b: Int) = a + b
}
addByName是傳名調(diào)用,addByValue是傳值調(diào)用仅醇。語(yǔ)法上可以看出冗美,使用傳名調(diào)用時(shí),在參數(shù)名稱和參數(shù)類型中間有一個(gè)=>
符號(hào)析二。
覺(jué)得傳名參數(shù)會(huì)很少用到粉洼,跟java的傳參習(xí)慣看起就可以了。
指定函數(shù)參數(shù)名
一般情況下函數(shù)調(diào)用參數(shù)甲抖,就按照函數(shù)定義時(shí)的參數(shù)順序一個(gè)個(gè)傳遞。但是我們也可以通過(guò)指定函數(shù)參數(shù)名心铃,并且不需要按照順序向函數(shù)傳遞參數(shù)准谚。
object Test {
def main(args: Array[String]) {
printInt(b=5, a=7);
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a );
println("Value of b : " + b );
}
}
可變參數(shù)
Scala 通過(guò)在參數(shù)的類型之后放一個(gè)*
星號(hào)來(lái)設(shè)置可變參數(shù)(可重復(fù)的參數(shù))。
object Test {
def main(args: Array[String]) {
printStrings("Java", "Scala", "Python");
}
def printStrings( args:String* ) = {
var i : Int = 0;
for( arg <- args ){
println("Arg value[" + i + "] = " + arg );
i = i + 1;
}
}
}
遞歸函數(shù)
object Test {
def main(args: Array[String]) {
for (i <- 1 to 10)
println(i + " 的階乘為: = " + factorial(i) )
}
def factorial(n: BigInt): BigInt = {
if (n <= 1)
1
else
n * factorial(n - 1)
}
}
默認(rèn)參數(shù)值
Scala 可以為函數(shù)參數(shù)指定默認(rèn)參數(shù)值去扣,使用了默認(rèn)參數(shù)柱衔,你在調(diào)用函數(shù)的過(guò)程中可以不需要傳遞參數(shù),這時(shí)函數(shù)就會(huì)調(diào)用它的默認(rèn)參數(shù)值愉棱,如果傳遞了參數(shù)唆铐,則傳遞值會(huì)取代默認(rèn)值。
object Test {
def main(args: Array[String]) {
println( "返回值 : " + addInt() );
}
def addInt( a:Int=5, b:Int=7 ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
高階函數(shù)
高階函數(shù)(Higher-Order Function)就是操作其他函數(shù)的函數(shù)奔滑。
Scala 中允許使用高階函數(shù), 高階函數(shù)可以使用其他函數(shù)作為參數(shù)艾岂,或者使用函數(shù)作為輸出結(jié)果。
- 將定義的函數(shù)賦給某變量
def func1(s:String):Unit = {
println(s)
}
val func2 = func1 _
val變量名 = 函數(shù)名+空格+_
這里函數(shù)名后面必須要有空格朋其,表明是函數(shù)的原型王浴。
高階函數(shù)是函數(shù)的參數(shù)也是函數(shù)。(因?yàn)楹瘮?shù)的參數(shù)可以是變量梅猿,而函數(shù)又可以賦值給變量氓辣,即函數(shù)和變量地位一樣,所以函數(shù)參數(shù)也可以是函數(shù))袱蚓。
scala> val iGreeting = (content:String) => println(content)
iGreeting: String => Unit = $$Lambda$1052/5181771@257f30f7
scala> def sendGreeting(func:(String) => Unit,content:String){func(content)}
sendGreeting: (func: String => Unit, content: String)Unit
scala> sendGreeting(iGreeting,"scala")
scala
首先我們定義了一個(gè)函數(shù)sendGreeting,這個(gè)函數(shù)有兩個(gè)參數(shù)钞啸,第一個(gè)參數(shù)是一個(gè)函數(shù),函數(shù)名是func,他有一個(gè)String類型的參數(shù)并且返回值是unit空的体斩;第二個(gè)參數(shù)是String類型的變量名為content的變量梭稚,函數(shù)體是將第二個(gè)參數(shù)作為第一個(gè)參數(shù)也就是函數(shù)func的參數(shù),來(lái)調(diào)用第一個(gè)函數(shù)硕勿,整個(gè)函數(shù)返回值為unit空哨毁。這里只要傳入的函數(shù)的格式與定義的一致就行。
scala> val array = Array(1,2,3,4,5,6)
array: Array[Int] = Array(1, 2, 3, 4, 5, 6)
scala> array.map(item => item*2)
res2: Array[Int] = Array(2, 4, 6, 8, 10, 12)
Array.map()作用源武,他會(huì)遍歷array中每一個(gè)元素扼褪,并將每個(gè)元素作為具體的值傳給map中的作為參數(shù)的函數(shù)。
高階函數(shù)有個(gè)非常有用的特性是類型推斷粱栖。其可以自動(dòng)推斷出參數(shù)的類型话浇,而且對(duì)于只有一個(gè)的參數(shù)的函數(shù),可以省略掉小括號(hào)闹究,并且在函數(shù)的參數(shù)作用的函數(shù)體內(nèi)只是用一次函數(shù)的輸入?yún)?shù)的值話幔崖,就可省略掉函數(shù)名,用下劃線(_)代替渣淤。
scala> val array = Array(1,2,3,4,5,6)
array: Array[Int] = Array(1, 2, 3, 4, 5, 6)
scala> array.map(_ * 2)
res3: Array[Int] = Array(2, 4, 6, 8, 10, 12)
scala> array.map(_ * 2).foreach(println(_))
2
4
6
8
10
12
scala> array.map(_ * 2).foreach(println _)
2
4
6
8
10
12
scala> array.map(_ * 2).foreach(println)
2
4
6
8
10
12
scala> array.map(_ * 2).filter(_ > 6).foreach(println)
8
10
12
內(nèi)嵌函數(shù)
我們可以在 Scala 函數(shù)內(nèi)定義函數(shù)赏寇,定義在函數(shù)內(nèi)的函數(shù)稱之為局部函數(shù)。
object Test {
def main(args: Array[String]) {
println( factorial(0) )
println( factorial(1) )
println( factorial(2) )
println( factorial(3) )
}
def factorial(i: Int): Int = {
def fact(i: Int, accumulator: Int): Int = {
if (i <= 1)
accumulator
else
fact(i - 1, i * accumulator)
}
fact(i, 1)
}
}
匿名函數(shù)
spark中大都用的是匿名函數(shù)(不為函數(shù)命名)价认,然后將其復(fù)制個(gè)一個(gè)變量嗅定。
匿名函數(shù)格式:
Val 變量名 = (參數(shù):類型) => 函數(shù)體
val func1 = (s:String) => println(s)
func1("scala")
偏應(yīng)用函數(shù)
Scala 偏應(yīng)用函數(shù)是一種表達(dá)式,你不需要提供函數(shù)需要的所有參數(shù)用踩,只需要提供部分渠退,或不提供所需參數(shù)。
import java.util.Date
object Test {
def main(args: Array[String]) {
val date = new Date
log(date, "message1" )
Thread.sleep(1000)
log(date, "message2" )
Thread.sleep(1000)
log(date, "message3" )
}
def log(date: Date, message: String) = {
println(date + "----" + message)
}
}
輸出結(jié)果:
$ scalac Test.scala
$ scala Test
Tue May 22 15:53:39 CST 2018----message1
Tue May 22 15:53:39 CST 2018----message2
Tue May 22 15:53:39 CST 2018----message3
log() 方法接收兩個(gè)參數(shù):date 和 message脐彩。我們?cè)诔绦驁?zhí)行時(shí)調(diào)用了三次碎乃,參數(shù) date 值都相同,message 不同惠奸。
我們可以使用偏應(yīng)用函數(shù)優(yōu)化以上方法梅誓,綁定第一個(gè) date 參數(shù),第二個(gè)參數(shù)使用下劃線(_)替換缺失的參數(shù)列表佛南,并把這個(gè)新的函數(shù)值的索引的賦給變量证九。
import java.util.Date
object Test {
def main(args: Array[String]) {
val date = new Date
val logWithDateBound = log(date, _ : String)
logWithDateBound("message1" )
Thread.sleep(1000)
logWithDateBound("message2" )
Thread.sleep(1000)
logWithDateBound("message3" )
}
def log(date: Date, message: String) = {
println(date + "----" + message)
}
}
輸出結(jié)果:
$ scalac Test.scala
$ scala Test
Tue May 22 15:53:39 CST 2018----message1
Tue May 22 15:53:39 CST 2018----message2
Tue May 22 15:53:39 CST 2018----message3
函數(shù)柯里化(Function Currying)
柯里化(Currying)指的是將原來(lái)接受兩個(gè)參數(shù)的函數(shù)變成新的接受一個(gè)參數(shù)的函數(shù)的過(guò)程。新的函數(shù)返回一個(gè)以原有第二個(gè)參數(shù)為參數(shù)的函數(shù)共虑。其也利用了閉包的特性愧怜。
定義一個(gè)函數(shù):
def add(x:Int,y:Int)=x+y
把這個(gè)函數(shù)變一下形:
def add(x:Int)(y:Int) = x + y
這種方式(過(guò)程)就叫柯里化。
-
老子《道德經(jīng)》第三十五章妈拌,老子故里拥坛,中國(guó)鹿邑蓬蝶。 ?