日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Scala函数式编程(三) scala集合和函数

發布時間:2023/11/27 生活经验 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Scala函数式编程(三) scala集合和函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前情提要:

scala函數式編程(二) scala基礎語法介紹

scala函數式編程(二) scala基礎語法介紹

前面已經稍微介紹了scala的常用語法以及面向對象的一些簡要知識,這次是補充上一章的,主要會介紹集合和函數。

注意噢,函數和方法是不一樣的,方法是在類里面定義的,函數是可以單獨存在的(嚴格來說,在scala內部,每個函數都是一個類)

一.scala集合介紹

還記得上一章介紹的object的apply方法嗎,很多數據結構其實都用到了它,從而讓我們可以直接用List(...)這樣來新建一個List,而不用自己手動new一個。

PS:注意,scala默認的數據結構都是不可變的,就是說一個List,沒法刪除或增加新的元素。當然,也有“可變”的數據結構,后面會介紹到。

1.1 List

得益于apply方法,我們可以不通過new來新建數據結構。

//通過工廠,新建一個List,這個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這個概念在python應用比較廣泛,它可以將多種數據類型(Int,String,Double等)打包在一起

scala> val tup = (1,1,2.1,"tuple",'c')  //將多種不同數據結構打包一起,可以有重復
tup: (Int, Int, Double, String, Char) = (1,1,2.1,tuple,c)

但在scala中,Tuple是有長度限制的,那就是一個Tuple最多只能有22個元素。

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)//一個Tuple超過22個元素,報錯了
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)

可以看到上面,一但一個Tuple超過22個元素,就會報錯了。至于為什么是22這個神奇的數字,好像一直沒有一個統一的論調。有人開玩笑的說23才對,因為有部電影的名字叫《The Number 23》~~

1.3 Map和Option

在說Map之前,需要先介紹Option,因為Map里面存的就是Option,這個后面介紹。

Option翻譯過來是選項,本質上,它也確實是一個選項,用來告訴你存不存在。還是用實例說明吧:

//Option里面可以存普通數據類型
scala> val optionInt = Option(1)
optionInt: Option[Int] = Some(1)scala> optionInt.get
res8: Int = 1
//但一個Option也可能為空
scala> val optionNone = Option(null)
optionNone: Option[Null] = None
//當Option里面是空的時候,是get不出東西的,還會報錯
scala> optionNone.get
java.util.NoSuchElementException: None.getat scala.None$.get(Option.scala:347)at scala.None$.get(Option.scala:345)... 32 elided
//這個時候可以判斷它就是空的
scala> optionNone.isEmpty
res11: Boolean = true
//但可以用getOrElse()方法,當Option里面有東西的時候,就返回那個東西,如果沒有東西,就返回getOrElse()的參數的內容
scala> optionNone.getOrElse("this is null")  //里面沒東西
res12: String = this is null
scala> optionInt.getOrElse("this is null") //里面有東西
res15: Any = 1

再說Map,Map里面的value的類型并不是你賦的那個數據類型,而是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] = Nonescala> map.get("test3").getOrElse("this is null")
res18: Any = this is null

這樣的好處是什么呢?還記得在java里面,每次都得為java為空的情況做判斷的痛苦嗎。在scala,這些煩惱通通不存在。有了Option,媽媽再也不用擔心NullPointerException啦。

1.4 常用函數組合子

匿名函數

將集合的函數組合子,那肯定得先將匿名函數。如果有用過python中的lambda表達式,那應該就很了解這種方式了。

前面說到,在scala中,函數就是對象,匿名函數也是函數。舉個簡單的例子:

//創建一個匿名函數
scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>scala> addOne(1)
res4: Int = 2

注意,函數里面是可以不用return的,最后面的那個x+1就是匿名函數的返回值了。

map,reduce

因為hadoop的出現,MapReduce都被說爛了。雖然Hadoop的Mapreduce起源自函數式的map和reduce,但兩者其實是不一樣的。感興趣的可以看看我之前寫過的一篇:從分治算法到 Hadoop MapReduce

函數式里面的map呢,粗略的說,其實相當一個有返回值的for循環。

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

至于reduce呢,也是像for循環,只是不像map每次循環是當前元素,reduce除了當前元素,還有上一次循環的結果,還是看看例子吧:

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

比如上面的例子,有1,2,3三個數。第一次循環的兩個是1,2,1+2就等于3,第二次循環就是上次的結果3和原本的3,3+3等于6,結果就是6。

filter

filter故名思意,就是過濾的意思,可以在filter中傳進去一個匿名函數,返回布爾值。返回true的則保留,返回false的丟棄。

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

foldLeft

這個和reduce類似,也是遍歷,除了當前元素,還有上一次迭代的結果。區別在于foldLeft有一個初始值。

scala> val numbers = List(1, 2, 3, 4)
numbers: List[Int] = List(1, 2, 3, 4)//(m: Int, n: Int) => m + n這部分是一個匿名函數
scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n)
res30: Int = 10

二.scala函數

在最前面有介紹到,函數就是對象。那為什么函數能直接運行呢?這其實得益于object的apply這個語法糖。

偏函數

偏函數(PartialFunction),從某種意義上來說,偏函數也是scala中挺重要的一個語法糖。

偏函數它長這樣:

PartialFunction[A, B] //接收一個A類型的參數,返回B類型的參數

值得一提的是,scala中有一個關鍵字case,就是使用偏函數。繼續舉例子:

//下面的case就是一個偏函數PartialFunction[Int, String]
scala> val one: PartialFunction[Int, String] = { case 1 => "one" }
one: PartialFunction[Int,String] = <function1>scala> one(1)
res11: String = Intscala> one("one")
<console>:13: error: type mismatch;found   : String("one")required: Intone("one")

case關鍵字會匹配符合條件的類型或值,如果不符合,會報錯。內部實現就不介紹了,知道是個常用語法糖就好。

而這個case關鍵字也正是實現scala模式匹配中,必不可少的一環,有興趣的童鞋可以看看我這篇:scala模式匹配詳細解析

這里再說一個常見應用吧,函數組合子中有一個collect,它需要的參數就是一個偏函數。下面看個有意思的例子:

//這個list里面的Any類型
scala> val list:List[Any] = List(1, 3, 5, "seven")
list: List[Any] = List(1, 3, 5, seven)//使用map會報錯,因為map接收的參數是普通函
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函數就可以,因為collect接收的參數是偏函數,它會自動使用偏函數的一些特性,所以可以自動過濾掉不符合的數據類型
scala> list.collect { case i: Int => i + 1 }
res15: List[Int] = List(2, 4, 6)

因為collect接收的參數是偏函數,它會自動使用偏函數的特性,自動過濾不符合的數據類型,而map就做不到。

部分應用函數

所謂部分應用的意思,就是說,當調用一個函數時,可以僅傳遞一部分參數。而這樣會生成一個新的函數,來個實例看看吧:

//定義一個打印兩個輸出參數的函數
scala> def partial(i:Int,j:Int) : Unit = {|     println(i)|     println(j)| }
partial: (i: Int,j: Int)Unit//賦一個值給上面那個函數,另一個參數不賦值,生成一個新的函數
scala> val partialFun = partial(5,_:Int)
partialFun: Int => Unit = <function1>//只要一個參數就可以調用啦
scala> partialFun(10)
5
10

部分應用函數,主要是作用是代碼復用,同時也能夠增加一定的代碼可讀性。

當然還有更多有意思的用法,后面有機會說到再說。

函數柯里化

剛開始,聽到柯里化的時候很奇怪。柯里?啥玩意?

后來才知道,其實柯里是從curry音譯過來的,這個是個人名,就是發明了柯里化的發明人。

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

看看具體是怎么使用吧:

//我們知道函數可以這樣定義,讓它接收兩個參數
scala> def curring(i:Int)(j: Int): Boolean = {false}
curring: (i: Int)(j: Int)Boolean//可以把這個函數賦值給一個變量,注意變量的類型
scala> val curringVal:(Int => (Int => Boolean)) = curring _
curringVal: Int => (Int => Boolean) = <function1>//可以讓這個變量接收一個參數,又變成另一個函數了
scala> val curringVal_1 = curringVal(5)
curringVal_1: Int => Boolean = <function1>//再用這個變量接收一個參數,終于能返回結果了
scala> curringVal_1(10)
res32: Boolean = false

柯里化其實是把一個函數變成一個調用鏈的過程,和上面的部分應用函數看起來有點像。

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

結語:

此次介紹的是scala集合的一些內容,以及一些函數的特性,再說一遍,函數其實就是對象。

我一直有一種觀點,在學習新的東西的時候,一些偏固定規則的東西,比如語法。不用全部記熟,只要知道大概原理,有個映像就行。

比如說scala的函數式編程,或是java的OOP,不需要抱有先把語法學完,再學習相關的編程理念,這在我看來是有點本末倒置了。

我一般的做法,是先熟悉大概的語法,然后去學習語言的精髓。當碰到不懂的時候,再反過來查詢具體的語法,有了目標之后,語法反而變得不是那么枯燥了。

以上只是我的一些個人看法,那么本篇到此就結束了。

以上~~

轉載于:https://www.cnblogs.com/listenfwind/p/11593498.html

總結

以上是生活随笔為你收集整理的Scala函数式编程(三) scala集合和函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。