[英]Scala, make my loop more functional
我正在努力減少像Java一樣編寫Scala(2.8)的程度。 這是我遇到的問題的簡化。 您能否建議改進我的“功能更強大”的解決方案?
變換地圖
val inputMap = mutable.LinkedHashMap(1->'a',2->'a',3->'b',4->'z',5->'c')
通過丟棄值為'z'的任何條目並在遇到它們時索引字符
第一次嘗試
var outputMap = new mutable.HashMap[Char,Int]()
var counter = 0
for(kvp <- inputMap){
val character = kvp._2
if(character !='z' && !outputMap.contains(character)){
outputMap += (character -> counter)
counter += 1
}
}
第二次嘗試(不是更好,但使用不可變的地圖和'foreach')
var outputMap = new immutable.HashMap[Char,Int]()
var counter = 0
inputMap.foreach{
case(number,character) => {
if(character !='z' && !outputMap.contains(character)){
outputMap2 += (character -> counter)
counter += 1
}
}
}
更好的解決方案:
inputMap.toList.filter(_._2 != 'z').map(_._2).distinct.zipWithIndex.toMap
我發現這個解決方案比arjan更簡單:
inputMap.values.filter(_ != 'z').toSeq.distinct.zipWithIndex.toMap
個別步驟:
inputMap.values // Iterable[Char] = MapLike(a, a, b, z, c)
.filter(_ != 'z') // Iterable[Char] = List(a, a, b, c)
.toSeq.distinct // Seq[Char] = List(a, b, c)
.zipWithIndex // Seq[(Char, Int)] = List((a,0), (b,1), (c,2))
.toMap // Map[Char, Int] = Map((a,0), (b,1), (c,2))
請注意,您的問題本身並不涉及地圖作為輸入,因為您只是丟棄了密鑰。 如果我正在編寫這個,我可能會寫一個像這樣的函數
def buildIndex[T](s: Seq[T]): Map[T, Int] = s.distinct.zipWithIndex.toMap
並將其作為
buildIndex(inputMap.values.filter(_ != 'z').toSeq)
首先,如果你在功能上這樣做,你應該使用不可變的地圖。
然后,為了擺脫某些東西,你使用filter
方法:
inputMap.filter(_._2 != 'z')
最后,要進行重新映射,您可以使用zipWithIndex
的值(但作為一組), zipWithIndex
計數,然后轉換回地圖:
inputMap.filter(_._2 != 'z').values.toSet.zipWithIndex.toMap
由於價值的順序無論如何都不會被保留*,可能是因為訂單可能已經使用集合轉換再次洗牌並不重要。
編輯:有一個類似的更好的解決方案; 看到Arjan的。 假設(*)是錯誤的,因為它是LinkedHashMap。 所以你需要保留秩序,這是Arjan的解決方案所做的。
我會像這樣創建一些“管道”,但這有很多操作,可能會縮短。 這兩個List.map
可以放在一個,但我想你有一個大概。
inputMap
.toList // List((5,c), (1,a), (2,a), (3,b), (4,z))
.sorted // List((1,a), (2,a), (3,b), (4,z), (5,c))
.filterNot((x) => {x._2 == 'z'}) // List((1,a), (2,a), (3,b), (5,c))
.map(_._2) // List(a, a, b, c)
.zipWithIndex // List((a,0), (a,1), (b,2), (c,3))
.map((x)=>{(x._2+1 -> x._1)}) // List((1,a), (2,a), (3,b), (4,c))
.toMap // Map((1,a), (2,a), (3,b), (4,c))
在列表上執行這些操作會保持元素的排序。
編輯:我誤讀了OP問題 - 認為你想要運行長度編碼。 以下是我對您實際問題的看法:
val values = inputMap.values.filterNot(_ == 'z').toSet.zipWithIndex.toMap
編輯2:如評論中所述,如果保留順序很重要,請使用toSeq.distinct或類似內容。
val values = inputMap.values.filterNot(_ == 'z').toSeq.distinct.zipWithIndex.toMap
根據我的經驗,我發現地圖和功能語言並不好看。 您將注意到目前為止所有答案都以某種方式涉及將地圖轉換為列表,過濾列表,然后將列表轉回地圖。
我認為這是由於地圖本質上是可變數據結構。 考慮在構建列表時,列表的基礎結構在附加新元素時不會更改,如果是真實列表,則追加是一個常量O(1)操作。 而對於地圖而言,當添加新元素時,地圖的內部結構會發生巨大變化,即。 當加載因子變得太高並且添加算法調整地圖大小時。 通過這種方式,由於引入新的鍵/值對可能產生的副作用,函數式語言不能僅創建一系列值並將其彈出到地圖中。
也就是說,我仍然認為應該更好地支持過濾,映射和折疊/縮小地圖。 由於我們從地圖開始,我們知道地圖的最大尺寸,並且應該很容易創建一個新地圖。
如果你想要掌握功能性編程,那么我建議開始轉向地圖。 堅持使用函數式語言設計的東西 - 列表操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.