簡體   English   中英

Scala,讓我的循環功能更強大

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM