Scala函數(shù)式編程(三) scala集合和函數(shù)

前情提要:

scala函數(shù)式編程(二) scala基礎(chǔ)語(yǔ)法介紹

scala函數(shù)式編程(二) scala基礎(chǔ)語(yǔ)法介紹

前面已經(jīng)稍微介紹了scala的常用語(yǔ)法以及面向?qū)ο蟮囊恍┖?jiǎn)要知識(shí)康愤,這次是補(bǔ)充上一章的,主要會(huì)介紹集合和函數(shù)虏杰。

注意噢,函數(shù)和方法是不一樣的伏嗜,方法是在類里面定義的垮斯,函數(shù)是可以單獨(dú)存在的(嚴(yán)格來(lái)說(shuō)草戈,在scala內(nèi)部,每個(gè)函數(shù)都是一個(gè)類)

一.scala集合介紹

還記得上一章介紹的object的apply方法嗎炕婶,很多數(shù)據(jù)結(jié)構(gòu)其實(shí)都用到了它姐赡,從而讓我們可以直接用List(...)這樣來(lái)新建一個(gè)List,而不用自己手動(dòng)new一個(gè)柠掂。

PS:注意项滑,scala默認(rèn)的數(shù)據(jù)結(jié)構(gòu)都是不可變的,就是說(shuō)一個(gè)List涯贞,沒(méi)法刪除或增加新的元素枪狂。當(dāng)然,也有“可變”的數(shù)據(jù)結(jié)構(gòu)宋渔,后面會(huì)介紹到州疾。

1.1 List

得益于apply方法,我們可以不通過(guò)new來(lái)新建數(shù)據(jù)結(jié)構(gòu)皇拣。

//通過(guò)工廠严蓖,新建一個(gè)List,這個(gè)List是不可變的
scala> val numbers = List(1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
numbers: List[Int] = List(1, 2, 3, 4, 5, 1, 2, 3, 4, 5)

1.2 元組Tuple

Tuple這個(gè)概念在python應(yīng)用比較廣泛氧急,它可以將多種數(shù)據(jù)類型(Int,String,Double等)打包在一起

scala> val tup = (1,1,2.1,"tuple",'c')  //將多種不同數(shù)據(jù)結(jié)構(gòu)打包一起谈飒,可以有重復(fù)
tup: (Int, Int, Double, String, Char) = (1,1,2.1,tuple,c)

但在scala中,Tuple是有長(zhǎng)度限制的态蒂,那就是一個(gè)Tuple最多只能有22個(gè)元素。

scala> val tup = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)
tup: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)

//一個(gè)Tuple超過(guò)22個(gè)元素费什,報(bào)錯(cuò)了
scala> val tup = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)
<console>:1: error: too many elements for tuple: 23, allowed: 22
val tup = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)

可以看到上面钾恢,一但一個(gè)Tuple超過(guò)22個(gè)元素,就會(huì)報(bào)錯(cuò)了鸳址。至于為什么是22這個(gè)神奇的數(shù)字瘩蚪,好像一直沒(méi)有一個(gè)統(tǒng)一的論調(diào)。有人開(kāi)玩笑的說(shuō)23才對(duì)稿黍,因?yàn)橛胁侩娪暗拿纸小禩he Number 23》~~

1.3 Map和Option

在說(shuō)Map之前疹瘦,需要先介紹Option,因?yàn)镸ap里面存的就是Option巡球,這個(gè)后面介紹言沐。

Option翻譯過(guò)來(lái)是選項(xiàng)邓嘹,本質(zhì)上,它也確實(shí)是一個(gè)選項(xiàng)险胰,用來(lái)告訴你存不存在汹押。還是用實(shí)例說(shuō)明吧:

//Option里面可以存普通數(shù)據(jù)類型
scala> val optionInt = Option(1)
optionInt: Option[Int] = Some(1)

scala> optionInt.get
res8: Int = 1
//但一個(gè)Option也可能為空
scala> val optionNone = Option(null)
optionNone: Option[Null] = None
//當(dāng)Option里面是空的時(shí)候,是get不出東西的起便,還會(huì)報(bào)錯(cuò)
scala> optionNone.get
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  ... 32 elided
//這個(gè)時(shí)候可以判斷它就是空的
scala> optionNone.isEmpty
res11: Boolean = true
//但可以用getOrElse()方法棚贾,當(dāng)Option里面有東西的時(shí)候,就返回那個(gè)東西榆综,如果沒(méi)有東西妙痹,就返回getOrElse()的參數(shù)的內(nèi)容
scala> optionNone.getOrElse("this is null")  //里面沒(méi)東西
res12: String = this is null
scala> optionInt.getOrElse("this is null") //里面有東西
res15: Any = 1

再說(shuō)Map,Map里面的value的類型并不是你賦的那個(gè)數(shù)據(jù)類型鼻疮,而是Option怯伊。即Map(key -> Option,key1 -> Option)。

scala> val map = Map("test1" -> 1,"test2" -> 2)
map: scala.collection.immutable.Map[String,Int] = Map(test1 -> 1, test2 -> 2)

scala> map.get("test1")
res16: Option[Int] = Some(1)

scala> map.get("test3")
res17: Option[Int] = None

scala> map.get("test3").getOrElse("this is null")
res18: Any = this is null

這樣的好處是什么呢陋守?還記得在java里面震贵,每次都得為java為空的情況做判斷的痛苦嗎。在scala水评,這些煩惱通通不存在猩系。有了Option,媽媽再也不用擔(dān)心NullPointerException啦中燥。

1.4 常用函數(shù)組合子

匿名函數(shù)

將集合的函數(shù)組合子寇甸,那肯定得先將匿名函數(shù)。如果有用過(guò)python中的lambda表達(dá)式疗涉,那應(yīng)該就很了解這種方式了拿霉。

前面說(shuō)到,在scala中咱扣,函數(shù)就是對(duì)象绽淘,匿名函數(shù)也是函數(shù)。舉個(gè)簡(jiǎn)單的例子:

//創(chuàng)建一個(gè)匿名函數(shù)
scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>

scala> addOne(1)
res4: Int = 2

注意闹伪,函數(shù)里面是可以不用return的沪铭,最后面的那個(gè)x+1就是匿名函數(shù)的返回值了。

map偏瓤,reduce

因?yàn)閔adoop的出現(xiàn)杀怠,MapReduce都被說(shuō)爛了。雖然Hadoop的Mapreduce起源自函數(shù)式的map和reduce厅克,但兩者其實(shí)是不一樣的赔退。感興趣的可以看看我之前寫過(guò)的一篇:從分治算法到 Hadoop MapReduce

函數(shù)式里面的map呢,粗略的說(shuō),其實(shí)相當(dāng)一個(gè)有返回值的for循環(huán)硕旗。

scala> val list = List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> list.map(_ + 1)   //這里的(_+1)其實(shí)就是一個(gè)匿名函數(shù) //讓List中每一個(gè)元素+1窗骑,并返回
res29: List[Int] = List(2, 3, 4)

至于reduce呢,也是像for循環(huán)卵渴,只是不像map每次循環(huán)是當(dāng)前元素慧域,reduce除了當(dāng)前元素,還有上一次循環(huán)的結(jié)果浪读,還是看看例子吧:

scala> list.reduce((i,j) => i + j)  //兩個(gè)兩個(gè)一起循環(huán)昔榴,這里是讓兩個(gè)相加
res28: Int = 6

比如上面的例子,有1,2,3三個(gè)數(shù)碘橘。第一次循環(huán)的兩個(gè)是1,2互订,1+2就等于3,第二次循環(huán)就是上次的結(jié)果3和原本的3痘拆,3+3等于6仰禽,結(jié)果就是6。

filter

filter故名思意纺蛆,就是過(guò)濾的意思吐葵,可以在filter中傳進(jìn)去一個(gè)匿名函數(shù),返回布爾值桥氏。返回true的則保留温峭,返回false的丟棄。

scala> val numbers = List(1, 2, 3, 4)
numbers: List[Int] = List(1, 2, 3, 4)

//過(guò)濾出余2等于0的
scala> numbers.filter((i: Int) => i % 2 == 0)
res0: List[Int] = List(2, 4)

foldLeft

這個(gè)和reduce類似字支,也是遍歷凤藏,除了當(dāng)前元素,還有上一次迭代的結(jié)果堕伪。區(qū)別在于foldLeft有一個(gè)初始值揖庄。

scala> val numbers = List(1, 2, 3, 4)
numbers: List[Int] = List(1, 2, 3, 4)

//(m: Int, n: Int) => m + n這部分是一個(gè)匿名函數(shù)
scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n)
res30: Int = 10

二.scala函數(shù)

在最前面有介紹到,函數(shù)就是對(duì)象欠雌。那為什么函數(shù)能直接運(yùn)行呢蹄梢?這其實(shí)得益于object的apply這個(gè)語(yǔ)法糖。

偏函數(shù)

偏函數(shù)(PartialFunction)富俄,從某種意義上來(lái)說(shuō)禁炒,偏函數(shù)也是scala中挺重要的一個(gè)語(yǔ)法糖。

偏函數(shù)它長(zhǎng)這樣:

PartialFunction[A, B] //接收一個(gè)A類型的參數(shù)蛙酪,返回B類型的參數(shù)

值得一提的是,scala中有一個(gè)關(guān)鍵字case翘盖,就是使用偏函數(shù)桂塞。繼續(xù)舉例子:

//下面的case就是一個(gè)偏函數(shù)PartialFunction[Int, String]
scala> val one: PartialFunction[Int, String] = { case 1 => "one" }
one: PartialFunction[Int,String] = <function1>

scala> one(1)
res11: String = Int

scala> one("one")
<console>:13: error: type mismatch;
 found   : String("one")
 required: Int
       one("one")

case關(guān)鍵字會(huì)匹配符合條件的類型或值,如果不符合馍驯,會(huì)報(bào)錯(cuò)阁危。內(nèi)部實(shí)現(xiàn)就不介紹了玛痊,知道是個(gè)常用語(yǔ)法糖就好。

而這個(gè)case關(guān)鍵字也正是實(shí)現(xiàn)scala模式匹配中狂打,必不可少的一環(huán)擂煞,有興趣的童鞋可以看看我這篇:scala模式匹配詳細(xì)解析

這里再說(shuō)一個(gè)常見(jiàn)應(yīng)用吧,函數(shù)組合子中有一個(gè)collect趴乡,它需要的參數(shù)就是一個(gè)偏函數(shù)对省。下面看個(gè)有意思的例子:

//這個(gè)list里面的Any類型
scala> val list:List[Any] = List(1, 3, 5, "seven")
list: List[Any] = List(1, 3, 5, seven)

//使用map會(huì)報(bào)錯(cuò),因?yàn)閙ap接收的參數(shù)是普通函
scala> list.map { case i: Int => i + 1 }
scala.MatchError: seven (of class java.lang.String)
  at $anonfun$1.apply(<console>:13)
  at $anonfun$1.apply(<console>:13)
  at scala.collection.immutable.List.map(List.scala:277)
  ... 32 elided

  //但如果用collect函數(shù)就可以晾捏,因?yàn)閏ollect接收的參數(shù)是偏函數(shù)蒿涎,它會(huì)自動(dòng)使用偏函數(shù)的一些特性,所以可以自動(dòng)過(guò)濾掉不符合的數(shù)據(jù)類型
scala> list.collect { case i: Int => i + 1 }
res15: List[Int] = List(2, 4, 6)

因?yàn)閏ollect接收的參數(shù)是偏函數(shù)惦辛,它會(huì)自動(dòng)使用偏函數(shù)的特性劳秋,自動(dòng)過(guò)濾不符合的數(shù)據(jù)類型,而map就做不到胖齐。

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

所謂部分應(yīng)用的意思玻淑,就是說(shuō),當(dāng)調(diào)用一個(gè)函數(shù)時(shí)呀伙,可以僅傳遞一部分參數(shù)补履。而這樣會(huì)生成一個(gè)新的函數(shù),來(lái)個(gè)實(shí)例看看吧:

//定義一個(gè)打印兩個(gè)輸出參數(shù)的函數(shù)
scala> def partial(i:Int,j:Int) : Unit = {
     |     println(i)
     |     println(j)
     | }
partial: (i: Int,j: Int)Unit

//賦一個(gè)值給上面那個(gè)函數(shù)区匠,另一個(gè)參數(shù)不賦值干像,生成一個(gè)新的函數(shù)
scala> val partialFun = partial(5,_:Int)
partialFun: Int => Unit = <function1>

//只要一個(gè)參數(shù)就可以調(diào)用啦
scala> partialFun(10)
5
10

部分應(yīng)用函數(shù),主要是作用是代碼復(fù)用驰弄,同時(shí)也能夠增加一定的代碼可讀性麻汰。

當(dāng)然還有更多有意思的用法,后面有機(jī)會(huì)說(shuō)到再說(shuō)戚篙。

函數(shù)柯里化

剛開(kāi)始五鲫,聽(tīng)到柯里化的時(shí)候很奇怪〔砝蓿柯里位喂?啥玩意?

后來(lái)才知道乱灵,其實(shí)柯里是從curry音譯過(guò)來(lái)的塑崖,這個(gè)是個(gè)人名,就是發(fā)明了柯里化的發(fā)明人痛倚。

柯里化是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù)规婆,并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。

看看具體是怎么使用吧:

//我們知道函數(shù)可以這樣定義,讓它接收兩個(gè)參數(shù)
scala> def curring(i:Int)(j: Int): Boolean = {false}
curring: (i: Int)(j: Int)Boolean

//可以把這個(gè)函數(shù)賦值給一個(gè)變量抒蚜,注意變量的類型
scala> val curringVal:(Int => (Int => Boolean)) = curring _
curringVal: Int => (Int => Boolean) = <function1>

//可以讓這個(gè)變量接收一個(gè)參數(shù)掘鄙,又變成另一個(gè)函數(shù)了
scala> val curringVal_1 = curringVal(5)
curringVal_1: Int => Boolean = <function1>

//再用這個(gè)變量接收一個(gè)參數(shù),終于能返回結(jié)果了
scala> curringVal_1(10)
res32: Boolean = false

柯里化其實(shí)是把一個(gè)函數(shù)變成一個(gè)調(diào)用鏈的過(guò)程嗡髓,和上面的部分應(yīng)用函數(shù)看起來(lái)有點(diǎn)像操漠。

這幾個(gè)部分初次看可能不知道它究竟有什么用,其實(shí)這些功能的一個(gè)主要用途是函數(shù)式的依賴注入饿这。通過(guò)這部分技術(shù)可以把被依賴的函數(shù)以參數(shù)的形式傳遞給上層函數(shù)浊伙。限于篇幅這里就先省略,后面再介紹蛹稍。

結(jié)語(yǔ):

此次介紹的是scala集合的一些內(nèi)容吧黄,以及一些函數(shù)的特性,再說(shuō)一遍唆姐,函數(shù)其實(shí)就是對(duì)象拗慨。

我一直有一種觀點(diǎn),在學(xué)習(xí)新的東西的時(shí)候奉芦,一些偏固定規(guī)則的東西赵抢,比如語(yǔ)法。不用全部記熟声功,只要知道大概原理烦却,有個(gè)映像就行。

比如說(shuō)scala的函數(shù)式編程先巴,或是java的OOP其爵,不需要抱有先把語(yǔ)法學(xué)完,再學(xué)習(xí)相關(guān)的編程理念伸蚯,這在我看來(lái)是有點(diǎn)本末倒置了摩渺。

我一般的做法,是先熟悉大概的語(yǔ)法剂邮,然后去學(xué)習(xí)語(yǔ)言的精髓摇幻。當(dāng)碰到不懂的時(shí)候,再反過(guò)來(lái)查詢具體的語(yǔ)法挥萌,有了目標(biāo)之后绰姻,語(yǔ)法反而變得不是那么枯燥了。

以上只是我的一些個(gè)人看法引瀑,那么本篇到此就結(jié)束了狂芋。

以上~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市憨栽,隨后出現(xiàn)的幾起案子帜矾,更是在濱河造成了極大的恐慌辆影,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件黍特,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锯蛀,警方通過(guò)查閱死者的電腦和手機(jī)灭衷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)旁涤,“玉大人翔曲,你說(shuō)我怎么就攤上這事∨蓿” “怎么了瞳遍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)菌羽。 經(jīng)常有香客問(wèn)我掠械,道長(zhǎng),這世上最難降的妖魔是什么注祖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任猾蒂,我火速辦了婚禮,結(jié)果婚禮上是晨,老公的妹妹穿的比我還像新娘肚菠。我一直安慰自己,他們只是感情好罩缴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蚊逢。 她就那樣靜靜地躺著,像睡著了一般箫章。 火紅的嫁衣襯著肌膚如雪烙荷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天炉抒,我揣著相機(jī)與錄音奢讨,去河邊找鬼。 笑死焰薄,一個(gè)胖子當(dāng)著我的面吹牛拿诸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塞茅,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼亩码,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了野瘦?” 一聲冷哼從身側(cè)響起描沟,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤飒泻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后吏廉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體泞遗,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年席覆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了史辙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡佩伤,死狀恐怖聊倔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情生巡,我是刑警寧澤耙蔑,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站孤荣,受9級(jí)特大地震影響甸陌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盐股,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一邀层、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遂庄,春花似錦寥院、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至霹肝,卻和暖如春估蹄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沫换。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工臭蚁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讯赏。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓垮兑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親漱挎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子系枪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 關(guān)于譯者:這是一個(gè)流淌著滬江血液的純粹工程:認(rèn)真,是 HTML 最堅(jiān)實(shí)的梁柱磕谅;分享私爷,是 CSS 里最閃耀的一瞥雾棺;...
    iKcamp閱讀 1,394評(píng)論 0 2
  • 函數(shù)式編程 引言 Scala中的函數(shù)是Java中完全沒(méi)有的概念。因?yàn)镴ava是完全面向?qū)ο蟮木幊陶Z(yǔ)言衬浑,沒(méi)有任何面向...
    義焃閱讀 1,278評(píng)論 2 5
  • 這篇講義只講scala的簡(jiǎn)單使用捌浩,目的是使各位新來(lái)的同事能夠首先看懂程序,因?yàn)?scala 有的語(yǔ)法對(duì)于之前使用習(xí)...
    MrRobot閱讀 2,912評(píng)論 0 10
  • 桃花落工秩,閑池閣嘉栓,山盟雖在,錦書(shū)難托拓诸,莫,莫麻昼,莫奠支! 「函數(shù)(Function)」是函數(shù)式編程的基本單元。本文將重點(diǎn)討...
    劉光聰閱讀 4,013評(píng)論 6 13
  • Scala是一種函數(shù)式編程語(yǔ)言抚芦,它具有函數(shù)式編程范式的諸多特點(diǎn)倍谜。需要說(shuō)明的是,scala并不是一種純函數(shù)式編程語(yǔ)言...
    我是老薛閱讀 1,954評(píng)論 0 2