4

恐怕这是另一个菜鸟问题。

我想做的是使用 aMap来计算一个单词在诗歌中出现的频率……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 中,不可变数据结构比可变数据结构更可取valvar因此我面临一个困境:如果结果是,words应该是var(允许每次迭代使用新的 map 实例)存储在一个不可变 Map的同时words变成一个val意味着使用一个可变 Map的。

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

4

5 回答 5

10

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

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)
 }

关于折叠的维基百科条目提供了一些很好的说明示例。

于 2012-04-26T14:37:30.657 回答
2

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

最后,Travis Brown 的答案是针对您的具体问题的解决方案,我的更多是个人哲学。

于 2012-04-26T14:39:41.700 回答
2

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

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

通过应用连续的函数,您可以避免对 vars 和 mutables 的需要

于 2012-04-26T14:39:55.150 回答
1

接下来的事情要归功于其他地方(特别是特拉维斯和丹尼尔),但有一个更简单的班轮需要离开。

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

有一个简化,因为您不需要 stripMargin 因为正则表达式,正如 Daniel 所建议的那样处理边距字符。

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

于 2012-04-27T01:18:39.813 回答
1

这就是 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。

于 2012-04-26T21:40:34.350 回答