2 回答

TA貢獻(xiàn)1943條經(jīng)驗(yàn) 獲得超7個(gè)贊
Scala中既有函數(shù)也有方法,大多數(shù)情況下我們都可以不去理會(huì)他們之間的區(qū)別。但是有時(shí)候我們必須要了解他們之間的不同。
Scala中的方法跟Java的方法一樣,方法是組成類的一部分。方法有名字、類型簽名,有時(shí)方法上還有注解,以及方法的功能
實(shí)現(xiàn)代碼(字節(jié)碼)。
Scala中的函數(shù)是一個(gè)完整的對(duì)象。Scala中用22個(gè)特質(zhì)(trait)抽象出了函數(shù)的概念。這22特質(zhì)從Function1到Function22
如上圖中的Function10代表的是:有10個(gè)形參,返回值為R(協(xié)變)的函數(shù)。
Scala中的函數(shù)其實(shí)就是繼承了這些Trait的類的對(duì)象,如:我們通過函數(shù)字面量定義一個(gè)函數(shù)
其實(shí)上述函數(shù)的定義方式跟如下定義方式等同:
由于Function2是特質(zhì),不能直接new。上述new Function2[Int,Int,Int](){}其實(shí)是定義并實(shí)例化一個(gè)實(shí)現(xiàn)了Function2特質(zhì)的類的對(duì)象。
apply是scala中的語法糖:對(duì)一個(gè)對(duì)象obj上調(diào)用obj(),scala編譯器會(huì)轉(zhuǎn)換為obj.apply();在一個(gè)類clazz上調(diào)用clazz(),scala編譯器會(huì)轉(zhuǎn)
換為clazz_company_obj.apply(),其中clazz_company_obj為clazz的伴生對(duì)象。
具體的差異,總結(jié)為如下幾點(diǎn):
1.方法不能作為單獨(dú)的表達(dá)式而存在(參數(shù)為空的方法除外),而函數(shù)可以。如:
在如上的例子中,我們首先定義了一個(gè)方法m,接著有定義了一個(gè)函數(shù)f。接著我們把函數(shù)名(函數(shù)值)當(dāng)作最終表達(dá)式來用,由于f本身就是
一個(gè)對(duì)象(實(shí)現(xiàn)了FunctionN特質(zhì)的對(duì)象),所以這種使用方式是完全正確的。但是我們把方法名當(dāng)成最終表達(dá)式來使用的話,就會(huì)出錯(cuò)。
2.函數(shù)必須要有參數(shù)列表,而方法可以沒有參數(shù)列表
在如上的例子中,m1方法接受零個(gè)參數(shù),所以可以省略參數(shù)列表。而函數(shù)不能省略參數(shù)列表
3.方法名是方法條用,而函數(shù)名只是代表函數(shù)對(duì)象本身
這個(gè)比較容易理解。因?yàn)楸4婧瘮?shù)字面量的變量(又稱為函數(shù)名或者函數(shù)值)本身就是實(shí)現(xiàn)了FunctionN特質(zhì)的類的對(duì)象,要調(diào)用對(duì)象的apply
方法,就需要使用obj()的語法。所以函數(shù)名后面加括號(hào)才是調(diào)用函數(shù)。如下:
4.在需要函數(shù)的地方,如果傳遞一個(gè)方法,會(huì)自動(dòng)進(jìn)行ETA展開(把方法轉(zhuǎn)換為函數(shù))
如果我們直接把一個(gè)方法賦值給變量會(huì)報(bào)錯(cuò)。如果我們指定變量的類型就是函數(shù),那么就可以通過編譯,如下:
當(dāng)然我們也可以強(qiáng)制把一個(gè)方法轉(zhuǎn)換給函數(shù),這就用到了scala中的部分應(yīng)用函數(shù):
5.傳名參數(shù)本質(zhì)上是個(gè)方法
傳名參數(shù)實(shí)質(zhì)上是一個(gè)參數(shù)列表為空的方法,如下:
如上代碼實(shí)際上定義了一個(gè)方法m1,m1的參數(shù)是個(gè)傳名參數(shù)(方法)。由于對(duì)于參數(shù)為空的方法來說,方法名就是方法調(diào)用
,所以List(x,x)實(shí)際上是進(jìn)行了兩次方法調(diào)用。
由于List(x,x)是進(jìn)行了兩次方法調(diào)用,所以得到兩個(gè)不同的值。
如果我們稍微修改一下函數(shù)的m1的定義,把x先緩存起來,結(jié)果就會(huì)跟以前大不一樣。

TA貢獻(xiàn)2016條經(jīng)驗(yàn) 獲得超9個(gè)贊
部分應(yīng)用函數(shù)和偏函數(shù)是無關(guān)的。經(jīng)常把部分應(yīng)用函數(shù)(Partial Applied Function)和偏函數(shù)(Partial Function) 搞混。
總結(jié)如下:
部分應(yīng)用函數(shù)(Partial Applied Function)是缺少部分參數(shù)的函數(shù),是一個(gè)邏輯上概念
偏函數(shù)是只對(duì)函數(shù)定義域的一個(gè)子集進(jìn)行定義的函數(shù)。 scala中用scala.PartialFunction[-T, +S]類來表示
比如定義了一個(gè)函數(shù):def sum(x: Int)(y: Int) = x + y, 當(dāng)調(diào)用sum的時(shí)候,如果不提供所有的參數(shù)或某些參數(shù)還未知時(shí),比如sum _ , sum(3)(_: Int), sum(_: Int)(3), 這樣就生成了所謂的部分應(yīng)用函數(shù)。部分應(yīng)用函數(shù)只是邏輯上的一個(gè)表達(dá),scala編譯器會(huì)用Function1, Function2這些類來表示它.
下面這個(gè)變量signal引用了一個(gè)偏函數(shù)
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
}
這個(gè)signal所引用的函數(shù)除了0值外,對(duì)所有整數(shù)都定義了相應(yīng)的操作。 signal(0) 會(huì)拋出異常,因此使用前最好先signal.isDefinedAt(0)判斷一下。 偏函數(shù)主要用于這樣一種場景:對(duì)某些值現(xiàn)在還無法給出具體的操作(即需求還不明朗),也有可能存在幾種處理方式(視乎具體的需求);我們可以先對(duì)需求明確的部分進(jìn)行定義,比如上述除了0外的所有整數(shù)域,然后根據(jù)具體情況補(bǔ)充對(duì)其他域的定義,比如 :
val composed_signal: PartialFunction[Int,Int] = signal.orElse{
case 0 => 0
}
composed_signal(0) // 返回 0
或者對(duì)定義域進(jìn)行一定的偏移(假如需求做了變更, 1 為無效的點(diǎn))
val new_signal: Function1[Int, Int] = signal.compose{
case x => x - 1
}
new_signal(1) // throw exception
new_signal(0) // 返回 -1
new_signal(2) // 返回 1
還可以用andThen將兩個(gè)相關(guān)的偏函數(shù)串接起來
val another_signal: PartialFunction[Int, Int] = {
case 0 => 0
case x if x > 0 => x - 1
case x if x < 0 => x + 1
}
val then_signal = another_signal andThen signal
這里的then_signal 剔除了-1, 0, 1三個(gè)點(diǎn)的定義
- 2 回答
- 0 關(guān)注
- 1188 瀏覽
添加回答
舉報(bào)