在循环中填充地图[Scala]

时间:2012-04-26 14:25:26

标签: scala map for-loop

我担心这是另一个noob问题。

我想要做的是使用Map来计算单词出现在 poe ... m 中的频率,然后将结果打印到控制台。 我转到了以下代码,我相信它正在起作用(虽然可能不太习惯):

val poe_m="""Once upon a midnight dreary, while I pondered weak and weary,
            |Over many a quaint and curious volume of forgotten lore,
            |While I nodded, nearly napping, suddenly there came a tapping,
            |As of some one gently rapping, rapping at my chamber door.
            |`'Tis some visitor,' I muttered, `tapping at my chamber door -
            |Only this, and nothing more.'"""

val separators=Array(' ',',','.','-','\n','\'','`')
var words=new collection.immutable.HashMap[String,Int]
for(word<-poe_m.stripMargin.split(separators) if(!word.isEmpty))  
    words=words+(word.toLowerCase -> (words.getOrElse(word.toLowerCase,0)+1))

words.foreach(entry=>println("Word : "+entry._1+" count : "+entry._2))

据我所知,在Scala中, immutable 数据结构优先于可变,而val优先于var所以我'面临困境:如果要将结果存储在 immutable {{words中,var应该是Map(允许每个迭代使用一个新的map实例) 1}}将words转换为val意味着使用 mutable Map

有人可以告诉我处理这个存在问题的正确方法吗?

5 个答案:

答案 0 :(得分:10)

在这种情况下,您可以使用groupBymapValues

val tokens = poe_m.stripMargin.split(separators).filterNot(_.isEmpty)
val words = tokens.groupBy(w => w).mapValues(_.size)

更一般地说,这是一个折叠的工作:

 val words = tokens.foldLeft(Map.empty[String, Int]) {
   case (m, t) => m.updated(t, m.getOrElse(t, 0) + 1)
 }

Wikipedia entry on folds提供了一些很好的澄清示例。

答案 1 :(得分:2)

好吧,在函数式编程中,最好使用一些不可变对象并使用函数来更新它们(例如返回更新后的map的尾递归函数)。但是,如果你没有处理重负载,你应该更喜欢使用var的可变映射,而不是因为它更强大(即使我认为它应该是)但是因为它更容易使用。

最后,特拉维斯·布朗的答案是解决你的具体问题,我的更多是个人哲学。

答案 2 :(得分:2)

我也是Scala的菜鸟,因此,可能有更好的方法来做到这一点。我想出了以下内容:

poe_m.stripMargin.split(separators)
     .filter(x => !x.isEmpty)
     .groupBy(x => x).foreach {
        case(w,ws) => println(w + " " + ws.size)
     }

通过应用连续的函数,您可以避免使用变量和变量

答案 3 :(得分:1)

信誉在于其他地方(尤其是特拉维斯和丹尼尔),但是有一个更简单的单线需要离开。

val words = poe_m split "\\W+" groupBy identity mapValues {_.size}

有一个简化,你不需要stripMargin,因为Daniel建议的正则表达式也处理了边缘字符。

您可以保留_.isEmpty过滤以防止空字符串的边缘情况,如果需要,可以产生(“” - &gt; 1)。

答案 4 :(得分:0)

这是如何在Martin Odersky的“Scala编程:综合循序渐进指南,第2版”这本非常好的书中完成的:

def countWords(text: String) = {
  val counts = mutable.Map.empty[String, Int]
  for (rawWord <- text.split("[ ,!.]+")) {
    val word = rawWord.toLowerCase
    val oldCount = 
      if (counts.contains(word)) counts(word)
      else 0
    counts += (word -> (oldCount + 1))
  }
  counts
}

但是,它也使用了一个可变的Map。