sc_*_*ray 3 iteration scala map type-conversion
我试图在scala中使用不可变映射实现一个简单的wordcount(这是有意的),我试图完成它的方式如下:
虽然scanner.hasNext()是真的:
在迭代结束时,将使用所有值填充地图.
我的代码如下:
val wordMap = Map.empty[String,Int]
val input = new java.util.scanner(new java.io.File("textfile.txt"))
while(input.hasNext()){
val token = input.next()
val currentCount = wordMap.getOrElse(token,0) + 1
val wordMap = wordMap + (token,currentCount)
}
Run Code Online (Sandbox Code Playgroud)
ides是wordMap将在迭代结束时拥有所有wordCounts ...每当我尝试运行此代码片段时,我会得到以下异常
递归值wordMap需要类型.
有人可以指出为什么我得到这个例外,我该怎么做才能解决它?
谢谢
val wordMap = wordMap + (token,currentCount)
Run Code Online (Sandbox Code Playgroud)
该行重新定义了已定义的变量.如果你想这样做,你需要定义wordMap,var然后使用
wordMap = wordMap + (token,currentCount)
Run Code Online (Sandbox Code Playgroud)
虽然怎么样呢?:
io.Source.fromFile("textfile.txt") // read from the file
.getLines.flatMap{ line => // for each line
line.split("\\s+") // split the line into tokens
.groupBy(identity).mapValues(_.size) // count each token in the line
} // this produces an iterator of token counts
.toStream // make a Stream so we can groupBy
.groupBy(_._1).mapValues(_.map(_._2).sum) // combine all the per-line counts
.toList
Run Code Online (Sandbox Code Playgroud)
请注意,每行预聚合用于尝试减少所需的内存.一次计数整个文件可能太大了.
如果您的文件非常庞大,我建议使用Scala的并行集合或Hadoop(使用Scrunch或Scoobi等酷酷的Scala Hadoop包装器)并行执行此操作(因为字数统计很容易并行化).
编辑:详细说明:
好的,首先看一下flatMap的内部部分.我们取一个字符串,并将其拆分为空格:
val line = "a b c b"
val tokens = line.split("\\s+") // Array(a, b, c, a, b)
Run Code Online (Sandbox Code Playgroud)
现在identity is a function that just returns its argument, so if wegroupBy(identity)`,我们将每个不同的单词类型映射到每个单词标记:
val grouped = tokens.groupBy(identity) // Map(c -> Array(c), a -> Array(a), b -> Array(b, b))
Run Code Online (Sandbox Code Playgroud)
最后,我们想要计算每种类型的令牌数量:
val counts = grouped.mapValues(_.size) // Map(c -> 1, a -> 1, b -> 2)
Run Code Online (Sandbox Code Playgroud)
由于我们将其映射到文件中的所有行,因此我们最终得到每行的令牌计数.
那怎么flatMap办?好吧,它在每一行上运行令牌计数功能,然后将所有结果合并到一个大集合中.
假设文件是:
a b c b
b c d d d
e f c
Run Code Online (Sandbox Code Playgroud)
然后我们得到:
val countsByLine =
io.Source.fromFile("textfile.txt") // read from the file
.getLines.flatMap{ line => // for each line
line.split("\\s+") // split the line into tokens
.groupBy(identity).mapValues(_.size) // count each token in the line
} // this produces an iterator of token counts
println(countsByLine.toList) // List((c,1), (a,1), (b,2), (c,1), (d,3), (b,1), (c,1), (e,1), (f,1))
Run Code Online (Sandbox Code Playgroud)
所以现在我们需要将每一行的计数合并为一大组计数.该countsByLine变量是一个Iterator,所以它不具有groupBy方法.相反,我们可以将它转换为a Stream,这基本上是一个懒惰的列表.我们想要懒惰,因为我们不希望在开始之前将整个文件读入内存.然后这些groupBy组一起计算相同的单词类型.
val groupedCounts = countsByLine.toStream.groupBy(_._1)
println(groupedCounts.mapValues(_.toList)) // Map(e -> List((e,1)), f -> List((f,1)), a -> List((a,1)), b -> List((b,2), (b,1)), c -> List((c,1), (c,1), (c,1)), d -> List((d,3)))
Run Code Online (Sandbox Code Playgroud)
最后,我们可以通过从每个元组中获取第二个项目(计数)来总结每个单词类型的每一行的计数,并总结:
val totalCounts = groupedCounts.mapValues(_.map(_._2).sum)
println(totalCounts.toList)
List((e,1), (f,1), (a,1), (b,3), (c,3), (d,3))
Run Code Online (Sandbox Code Playgroud)
你有它.
| 归档时间: |
|
| 查看次数: |
861 次 |
| 最近记录: |