我担心这是另一个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
。
有人可以告诉我处理这个存在问题的正确方法吗?
答案 0 :(得分:10)
在这种情况下,您可以使用groupBy
和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)
}
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。