scala使用reduce和fold方法遍历集合的所有元素
Problem
????你想要遍歷有序集合的所有元素,并且隨著你對集合元素的遍歷,對比兩個相鄰的元素
Solution
????使用reduceLeft, foldLeft, reduceRight, foldRight來遍歷集合的元素,你的方法作用在相鄰的兩個元素上,從第一次要遍歷的兩個相鄰元素開始,把你的方法作用在這兩個元素上得到返回值,然后把你的方法繼續作用在返回值和集合中第三要遍歷的元素得到的返回值,再繼續和第四個要遍歷的元素作用。。。直到遍歷完最后一個元素為止:
scala>?val?a?=?Array(12,?6,?15,?2,?20,?9) a:?Array[Int]?=?Array(12,?6,?15,?2,?20,?9)scala>?a.reduceLeft(_?+?_) res32:?Int?=?64????這個例子是這樣的:12+6=18, 18+15=33, 33+2=35, 35+20=55, 55+9=64,就是對集合的所有元素求和
????接下來你會看到如何使用reduceLeft來計算集合元素的乘積和求最大最小值:
scala>?a.reduceLeft(_?*?_) res33:?Int?=?388800scala>?a.reduceLeft(_?min?_) res34:?Int?=?2scala>?a.reduceLeft(_?max?_) res35:?Int?=?20Show each step in the process
????我們來看看reduceLeft的執行細節:
val?findMax?=?(x:?Int,?y:?Int)?=>?{val?winner?=?x?max?yprintln(s"compared?$x?to?$y,?$winner?was?larger")winner } scala>?a.reduceLeft((x,y)?=>?findMax(x,y)) compared?12?to?6,?12?was?larger compared?12?to?15,?15?was?larger compared?15?to?2,?15?was?larger compared?15?to?20,?20?was?larger compared?20?to?9,?20?was?larger res38:?Int?=?20????上面的輸出信息展示了reduceLeft是如何遍歷集合中的每一個元素,并在每一步時是如何調用傳入的函數操作集合元素的。我們來總結一下reduceLeft的操作步驟:
-
reduceLeft開始調用findMax方來來比較集合的前兩個元素,12和6,findMax方法返回12,因為12>6
-
reduceLeft接下來使用第一次調用findMax的返回值12和集合的第三個元素15,調用findMax(12, 15),因為15>12所以findMax返回15
-
reduceLeft接下來用每一步執行的返回值和集合的下一個元素傳入findMax方法,返回較大的值,直到遍歷完集合的最后一個元素,返回最大值20
????我們來換種方法來模擬reduceLeft的功能:
//?you?provide?the?sequence?'seq'?and?the?function?'f' var?result?=?seq(0) for?(i?<-?1?until?seq.length)?{val?next?=?seq(i)result?=?f(result,?next) }????一個關于reduceLeft方法非常微妙并且重要的事項:傳入的方法的返回值類型必須是要和集合中存儲的數據類型相同的。這是非常必要的,因為reduceLeft會對比方法的返回值和集合中的下一個元素。
Working with other sequences and types
????正如你想象的,集合包涵的數據類型可以是任何你想要的。舉個例子,遍歷一個字符串序列通過一個方法來確定最長的活著最短的字符串:
foldLeft, reduceRight, and foldRight
????方法foldLeft與reduceLeft工作方法很想,但是它讓你指定一個值作為第一個元素。下面這個例子真是了求和算法,第一個是reduceLeft方法,后面是foldLeft方法,我們來看看它們的區別:
scala>?val?a?=?Array(1,?2,?3) a:?Array[Int]?=?Array(1,?2,?3)scala>?a.reduceLeft(_+_) res6:?Int?=?6scala>?a.foldLeft(100)(_+_) res7:?Int?=?106scala>?a.foldLeft(200)(_+_) res8:?Int?=?206????上面最后兩個例子中foldLeft分別使用了100,200作為首元素,這個值直接影響到了最終求和的結果。如果你還從沒有見過這樣的語法,就拿foldLeft接受兩個參數列表來解釋一下把。第一個參數表接受一個字段,種子值。第二個參數表是一個你想要運行的代碼塊。方法reduceRight和foldRight執行方式和reduceLeft和foldLeft一樣,但是它們是從集合的最后一個元素開始遍歷,然后從右到左,直到遍歷到集合的開始位置。
The difference between reduceLeft and reduceRight
????在許多算法中,你可能并不在意使用reduceLeft還是reduceRight。在這種情況下,你可以使用reduce方法代替。Scala文檔中對reduce的說明:“對元素的操作順序是不明的或者不確定的”。
????但是還有許多算法會對兩種方法返回不同的結果。舉個例子,divide函數:
val?divide?=?(x:?Double,?y:?Double)?=>?{val?result?=?x?/?yprintln(s"divided?$x?by?$y?to?yield?$result")result }scala>?def?divide(x:Double,?y:Double):Double?=?{|???val?result?=?x?/?y|???println(s"divided?$x?by?$y?to?yield?$result")|???result|?} divide:?(x:?Double,?y:?Double)Double????定義一個集合,分別查看調用reduceLeft和reduceRight的結果:
scala>?val?a?=?Array(1.0,?2.0,?3.0)scala>?a.reduceLeft((x,y)?=>?divide(x,y)) divided?1.0?by?2.0?to?yield?0.5 divided?0.5?by?3.0?to?yield?0.16666666666666666 res10:?Double?=?0.16666666666666666scala>?a.reduceRight((x,y)?=>?divide(x,y)) divided?2.0?by?3.0?to?yield?0.6666666666666666 divided?1.0?by?0.6666666666666666?to?yield?1.5 res11:?Double?=?1.5scanLeft and scanRight
????方法scanLeft和scanRight也會變了整個集合,類似于reduceLeft和reduceRight,但是它們返回一個集合而不是一個值。
????舉個例子,scanLeft產生一個集合,集合元素為從左到右遍歷遍歷集合并對集合元素調用操作函數產生返回值。為了理解這事如何工作的,我們新建一個帶有debug的方法:
????正如你看到的,scanLeft返回了一個新的集合,而不是一個值。方法scanRight以同樣的方式工作,不過是從右到左遍歷集合。
????那還有一些相關的方法,包含reduce,reduceLeftOption,reduceRightOption。
????如果你關心reduce方法的情況,那么運行這段代碼來看看吧:
總結
以上是生活随笔為你收集整理的scala使用reduce和fold方法遍历集合的所有元素的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: scala把序列分解成子集(group
- 下一篇: 利用 livy 远程提交 spark作业