溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Scala函數(shù)式編程基礎(chǔ)講解

發(fā)布時(shí)間:2021-07-20 11:44:06 來源:億速云 閱讀:182 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要講解了“Scala函數(shù)式編程基礎(chǔ)講解”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Scala函數(shù)式編程基礎(chǔ)講解”吧!

3 函數(shù)式編程基礎(chǔ)

?
在純函數(shù)式編程語言里,變量就像數(shù)學(xué)語言里的代數(shù)符合,一經(jīng)確定就不能改變。正是由于這種不可變性,使得函數(shù)和普通的值之間具有對等關(guān)系。這樣函數(shù)就跟普通的值一樣成為了“頭等公民”,可以像任何其他數(shù)據(jù)類型一樣被傳遞和操作。

scala不是完全的函數(shù)式編程語言,它在架構(gòu)的上層提倡采用面向?qū)ο缶幊?,而在對?shù)據(jù)和各種底層操作作中使用函數(shù)式編程,對變量的定義既可以使用val,也可以使用var,所以它并不要求變量不可變。但在實(shí)際操作中,scala建議多用val,少用var,這樣可以降低出錯(cuò)的概率。
?

函數(shù)的定義與使用

函數(shù)既然與普通的值對等,那么它也應(yīng)該擁有類型的區(qū)別。

  • 類型

    類型應(yīng)該明確函數(shù)的返回值類型,以及傳入?yún)?shù)的類型

  • 值是一個(gè)函數(shù)的具體實(shí)現(xiàn)

比如:

scala> val counter:(Int) => Int = {   value => value + 1}val counter: Int => Int = $Lambda$1089/230944166@1372696bscala> counter(5)val res8: Int = 6

上面定義的counter是一個(gè)函數(shù),第2行包括了它的傳入?yún)?shù)類型以及返回值類型定義,即 (Int) => Int

符號=>的左邊是該方法的傳入?yún)?shù)類型,右邊是方法的返回值類型。

等號"=" 后面大括號中的內(nèi)容就是方法體。

當(dāng)函數(shù)需要定義多個(gè)參數(shù)列表時(shí):

scala> def mult(factor:Int)(x:Int) = x*factordef mult(factor: Int)(x: Int): Intscala> mult(90)(5)val res9: Int = 450

高階函數(shù)

當(dāng)一個(gè)函數(shù)包含其它函數(shù)作為參數(shù)或作為返回值時(shí),該函數(shù)稱為高階函數(shù),可以說它是操作其它函數(shù)的函數(shù)。

scala> def sum(f:Int => Int, a:Int, b:Int):Int = {    if(a>b) 0 else f(a)+sum(f,a+1,b)}def sum(f: Int => Int, a: Int, b: Int): Intscala> sum(x=>x*x, 1, 5)val res10: Int = 55

上面函數(shù)的計(jì)算過程:

初始時(shí) a = 1, b = 5

sum(x=>x*x, 1, 5)

= f(1) + sum(f, 2, 5) = 1 + f(2) +sum(f, 3, 5) = 1 + 4 + f(3) + sum(4,5)

= 1 + 4 +9 + f(4) + sum (5,5) = 1 + 4 + 9 + 16 + f(5) + sum(6,5)

= 1+ 4 + 9 + 16 + 25 + 0 = 55

閉包

有些函數(shù)的執(zhí)行只依賴于傳入?yún)?shù)的值,與調(diào)用函數(shù)的上下文無關(guān)。當(dāng)函數(shù)執(zhí)行時(shí)依賴于聲明于函數(shù)外部的變量時(shí),則稱這個(gè)函數(shù)為閉包。

偏應(yīng)用函數(shù)和Curry化

  1. 偏應(yīng)用函數(shù)

    一個(gè)函數(shù)在特殊應(yīng)用場景下參數(shù)可能會多次取相同的值,將這個(gè)函數(shù)的部分參數(shù)整合為一個(gè)參數(shù),傳入一個(gè)新的函數(shù)中,這就稱為偏應(yīng)用函數(shù)。

    scala> def sum(a:Int, b:Int, c:Int):Int = {     a+b+c}def sum(a: Int, b: Int, c: Int): Intscala> sum(1,2,3)val res21: Int = 6scala> sum(1,2,4)val res22: Int = 7scala> val a = sum(1,2,_:Int)val a: Int => Int = $Lambda$1114/1028466661@5dd12d01scala> a(9)val res23: Int = 12


    定義一個(gè)新的變量a, 它將原本sum函數(shù)的第1、2個(gè)傳入?yún)?shù)的值固定了,然后我們調(diào)用的時(shí)候只需要向 a 傳入原本sum函數(shù)中的第3個(gè)參數(shù)就可以了。

  2. Curry化

    Curry化的函數(shù)是指一個(gè)函數(shù)擁有多個(gè)參數(shù)列表,并且每個(gè)參數(shù)列表中只有一個(gè)參數(shù)。

針對容器的操作

  1. 遍歷

    scala標(biāo)準(zhǔn)的容器遍歷方法為foreach方法。該方法的原型為:def foreach[U](f: Elem => U): Unit

    先來看一個(gè)例子:

    scala> var list = List(1,2,3)         //創(chuàng)建一個(gè)List容器var list: List[Int] = List(1, 2, 3)scala> var f = (i:Int) => println(i)var f: Int => Unit = $Lambda$1122/1187220855@4ad30ac5scala> list.foreach(f)123


    可以看到,foreach方法返回值類型為Unit,該函數(shù)接受一個(gè)函數(shù)f 作為參數(shù)。

    函數(shù)f 的傳入?yún)?shù)類型為Elem,即容器中元素的類型;然后返回值類型為Unit。

    在這個(gè)例子中,我們遍歷了List容器中的元素,其元素類型為Int。傳入foreach函數(shù)的函數(shù)參數(shù) f 的功能是打印元素。

    我們還可以使用中綴表示法來調(diào)用 foreach:格式為容器 foreach 應(yīng)用在容器上的方法

    scala> list foreach println123


  2. 映射

    映射是指通過對容器中的元素進(jìn)行某些運(yùn)算來生成一個(gè)新的容器。scala中有兩個(gè)典型的映射方法:map 方法和flatMap方法。

    • map方法對集合中每個(gè)元素進(jìn)行指定運(yùn)算生成新的元素,會返回一個(gè)與原容器同樣類型和大小的新容器。

      scala> val bao = List("chen","rui","bo")val bao: List[String] = List(chen, rui, bo)scala> bao.map(s => s.length)val res32: List[Int] = List(4, 3, 2)


    • flatMap方法對集合中每個(gè)元素進(jìn)行指定運(yùn)算,然后對于每個(gè)元素都會返回一個(gè)容器,最后把生成的所有容器合并成為一個(gè)容器并返回。返回容器的類型與原容器相同,但大小可能不同,其中元素類型也可能不同。

      scala> bao.flatMap(s => s.toList)val res36: List[Char] = List(c, h, e, n, r, u, i, b, o)


      ps: List是一個(gè)列表,會存儲重復(fù)元素。

      ?

  3. 過濾

    過濾,顧名思義就是根據(jù)實(shí)際需求對容器中的元素進(jìn)行篩選。scala中最經(jīng)典的過濾方法是 filter,exists以及find 。

    下面舉個(gè)例子來看看怎么使用filter :

    scala> val university = Map("XMU"->"Xiamen University","THU"->"Tsinghua University", "PKU"->"Peking University")val university: scala.collection.immutable.Map[String,String] = Map(XMU -> Xiamen University, THU -> Tsinghua University, PKU -> Peking University)scala> f = university filter{     kv => kv._2 contains "Xiamen"} val res39: scala.collection.immutable.Map[String,String] = Map(XMU -> Xiamen University)


    整個(gè)過程就是,首先創(chuàng)建一個(gè)Map容器,然后遍歷整個(gè)容器,查找值含有"Xiamen"的鍵值對,進(jìn)行過濾,得到一個(gè)新的Map容器。其中kv._2用于訪問某個(gè)鍵值對的值,相應(yīng)地,如果要訪問該鍵值對的鍵,則使用kv._1。

    類似的,exists 用于查找容器中是否含有某個(gè)元素,find 用于查找第一個(gè)符合條件的元素。

    ?

  4. 規(guī)約

    規(guī)約是對容器的元素進(jìn)行兩兩運(yùn)算,將其“規(guī)約”為一個(gè)值。

    先來看看最常用的reduce 方法:

    scala> val list = List(1,2,3,4,5)val list: List[Int] = List(1, 2, 3, 4, 5)scala> list.reduce(_+_)val res41: Int = 15


    reduce接收了一個(gè)二元函數(shù)f (帶有2個(gè)元素) 作為參數(shù),然后進(jìn)行運(yùn)算,最后結(jié)果為15,這個(gè)過程可以表示為:((((1+2)+3)+4)+5)

    所以reduce第一次接受的元素分別是1和2,然后是3和3,接著是6和4,最后是10和5,每次都將接受的兩個(gè)元素相加,然后再將相加的結(jié)果作為下一次接收的兩個(gè)元素中的第一個(gè),規(guī)約操作進(jìn)行到容器中的元素遍歷完成為止。

    ?

    ?

    • 對于List這樣的元素有序的容器做規(guī)約,reduce默認(rèn)的順序?yàn)閺淖蟮接?,若要自定義遍歷順序,可以使用reduceLeft或者reduceRight。規(guī)約結(jié)果跟進(jìn)行的運(yùn)算以及規(guī)約順序也有關(guān)系,加法和乘法遵循交換律和結(jié)合律,從左到右和從右到左并沒有區(qū)別,而對于減法就有區(qū)別了。

    • 對于Set這種元素?zé)o序的容器做規(guī)約,由于順序未定,其最后結(jié)果可能是不確定的。

    • 與reduce相似的規(guī)約方法還有fold。

  5. 拆分

    拆分操作將容器中的元素按照一定規(guī)則分割成多個(gè)子容器。常用的拆分方法有:partition、groupedBy、grouped和sliding。

    • partition方法接受一個(gè)布爾函數(shù),遍歷容器并使用該布爾函數(shù)判斷每個(gè)元素是否符合條件,然后以二元組的方式返回兩個(gè)容器,分別為滿足條件的元素集合和不滿足條件的元素集合。

      scala> val list = List(1,2,3,4,5,6,7)val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7)scala> list.partition(_<4)val res42: (List[Int], List[Int]) = (List(1, 2, 3),List(4, 5, 6, 7))


    • groupedBy方法接收一個(gè)返回Unit類型的函數(shù),用該函數(shù)對容器元素進(jìn)行遍歷,將返回值相同的元素作為一個(gè)子容器,并與該相同的值構(gòu)成一個(gè)HashMap鍵值對,最后返回的形式為:Map[U,C[T]]。

      scala> val gby = list.groupBy(x=>x%3)val gby: scala.collection.immutable.Map[Int,List[Int]] = HashMap(0 -> List(3, 6), 1 -> List(1, 4, 7), 2 -> List(2, 5))


    • grouped 方法接收一個(gè)整型參數(shù) n,按從左到右的順序?qū)⑷萜鲃澐譃槎鄠€(gè)大小為n的子容器,最后一個(gè)容器的大小可能不足n,然后返回由子容器構(gòu)成的迭代器。

      scala> val group = list.grouped(2)val group: Iterator[List[Int]] = <iterator>scala> group foreach println
      List(1, 2)List(3, 4)List(5, 6)List(7)


    • sliding方法接收一個(gè)整型參數(shù) n,按從左到右的順序?qū)⑷萜鹘厝槎鄠€(gè)長度為n的滑動(dòng)窗口,返回由子容器構(gòu)成的迭代器。

      scala> val slidingwindow = list.sliding(3)val slidingwindow: Iterator[List[Int]] = <iterator>scala> slidingwindow foreach println
      List(1, 2, 3)List(2, 3, 4)List(3, 4, 5)List(4, 5, 6)List(5, 6, 7)

      ??

感謝各位的閱讀,以上就是“Scala函數(shù)式編程基礎(chǔ)講解”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Scala函數(shù)式編程基礎(chǔ)講解這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI