Scala入門

眾所周知,scala作為一門極客型的函數(shù)式編程語言,支持的特性包括:

  • 函數(shù)擁有“一等公民”身份;
  • 支持匿名函數(shù)(函數(shù)字面量)
  • 支持高階函數(shù)
  • 支持閉包
  • 部分應(yīng)用函數(shù)
  • 柯里化

首先需要指出,在scala中有方法函數(shù)對(duì)象兩種形態(tài),方法即是通過def關(guān)鍵字定義的函數(shù),而函數(shù)對(duì)象則是通過將方法轉(zhuǎn)換而來,或lambda賦值而來。

1. 從“一等公民”說起

很多稍微了解過函數(shù)式編程的人可能都聽說過“一等公民”這種說法,但卻很少有人能明明白白地說出究竟什么是“一等公民”。這里我做個(gè)類比你馬上就明白了:現(xiàn)實(shí)中,什么樣的人能被當(dāng)做一等公民?首先,他必須是個(gè)獨(dú)立的個(gè)體——依賴父母或朋友才能生存的人肯定不能被當(dāng)做公民,更不用說一等了;其次,這個(gè)人必須擁有足夠的自由——既能上九天攬?jiān)?,又可下五洋捉鱉,方才能是一等公民。對(duì)應(yīng)到我們的函數(shù)式編程,我們可以總結(jié)出幾個(gè)點(diǎn):

(1) 函數(shù)的定義和調(diào)用不依賴其他結(jié)構(gòu),例如C、python、js、scala,而反面典型就是java,因?yàn)閖ava的任何函數(shù)(方法)都必須定義在類、接口、枚舉(其實(shí)也是類)中,而且任何的方法調(diào)用都要通過對(duì)象、類的靜態(tài)方法或接口(jdk 1.8),方法不可能直接調(diào)用,必須依附于其他結(jié)構(gòu)而存在。所以這種情況下函數(shù)肯定不是“一等公民”。

(2)函數(shù)可以作為函數(shù)的參數(shù)、返回值,并可以對(duì)函數(shù)進(jìn)行變量賦值,而且函數(shù)的定義位置極度自由,任何代碼塊里又能定義函數(shù)。

現(xiàn)在我們?cè)賮砜磗cala,它完美地契合上邊所有的需求(但是注意,除了腳本形式的scala之外,其他的scala程序也只能包含在class或object中),scala中函數(shù)支持在函數(shù)內(nèi)部定義,而且使用lambda表達(dá)式定義的函數(shù)可以賦值給任何變量、常量,所有函數(shù)均可作為返回值、參數(shù)。

2. lambda表達(dá)式的學(xué)問

很多scala初學(xué)者都倒在了scala的lambda上,因?yàn)閟cala lambda的靈活多樣,導(dǎo)致很多時(shí)候你可能都看不懂。下面我們從最基本的講起:

最基本的:

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">val fun = (a:Int) => {a < 100 && a > 0}</pre>

當(dāng)r定義的參數(shù)為函數(shù)時(shí):

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">def fun1(f:String => Unit) = f("wangyalou")</pre>

我們可以方便地使用lambda傳入需要的函數(shù):

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">fun1((s:String)=>{println(s)})</pre>

注意了,一般人都不這么寫,因?yàn)樽鳛閰?shù)的lambda可~~~~準(zhǔn)備好我要開始啰!首先,省略掉可以推斷出來的類型參數(shù):

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">fun1((s)=>{println(s)})</pre>

當(dāng)只有一個(gè)參數(shù)時(shí),=>前的()可?。?/p>

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">fun1(s=>{println(s)})</pre>

還可再簡化,scala中可以用_代替只出現(xiàn)一次的參數(shù):

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">fun1(println(_)) 或 fun1(println _)</pre>

最后,我們甚至連下劃線都可以不要了:

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">fun1(println)</pre>

注意,最后的情況我們是利用了編譯器支持lambda的“eta轉(zhuǎn)換”,即在表達(dá)式只有一個(gè)參數(shù),且整個(gè)執(zhí)行部分就是一個(gè)函數(shù)調(diào)用時(shí),可以直接寫函數(shù)名

插一句:eta擴(kuò)展(eta-expression)是另一個(gè)東西,指的是將一個(gè)普通方法轉(zhuǎn)換為函數(shù)對(duì)象的過程:

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">val b= too(,,_)
val b= too _ //這也可以?是的
val b : (Int,Int,Int) =>Int = foo
</pre>

其中too為一個(gè)參數(shù)為3個(gè)Int的方法

但是,too(,,1)一定不是eta擴(kuò)展!

下劃線的用法博大精深,這里再給出一些例子:

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">例1:lambda作為參數(shù),可代替只用一次的參數(shù),且省掉“=>”
val nums = Array(1,2,3,4)
nums.filter(
>2)

運(yùn)行結(jié)果:
res31: Array[Int] = Array(3, 4)

</pre>

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">例2:lambda作為參數(shù),_可代替只用一次的參數(shù),且省掉“=>”</pre>

scala> def foo(f:(Int,Int)=>Int)(a:Int,b:Int) = f(a,b)
foo: (f: (Int, Int) => Int)(a: Int, b: Int)Int

scala> foo(+)(3,4)
res33: Int = 7

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">例3:lambda作為函數(shù)定義,可代替只用一次的參數(shù),且省掉“=>”,但這時(shí)要加上類型,因?yàn)檫@里無法推斷出“”的類型</pre>

val b = (:Int) + (:Int)

b(1,2)

運(yùn)行結(jié)果:

res32: Int = 3

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

3. 部分應(yīng)用函數(shù)(偏函數(shù))

一個(gè)例子足以說清楚:

<pre style="font-family: Courier New; font-size: 12px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word;">scala> def foo(a:Int, b:Int, c:Boolean) = if(c) a+b else a-b
foo: (a: Int, b: Int, c: Boolean)Int

scala> val foom = foo(:Int,:Int,false) foom: (Int, Int) => Int = <function2></pre>

類似于python中的偏函數(shù),這里將某個(gè)參數(shù)確定,其他參數(shù)用"_"代替并指明其類型,注意一定要指明類型?。。?!不然就成了eta擴(kuò)展失敗的案例了!!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容