我有一个巨大的文件(不适合内存),它由两列(key
和value
)分隔,并在key
列上预先排序。我需要为键的所有值调用一个函数并写出结果。为简单起见,可以假设值是数字,函数是加法。
所以,给出一个输入:
A 1
A 2
B 1
B 3
输出结果为:
A 3
B 4
对于这个问题,我对阅读/编写文件不是那么感兴趣,而是在列表理解方面更多。尽管整个内容(输入和输出)并不适合内存,但这很重要。我是Scala的新手,来自Java,我很感兴趣的是功能/ Scala方法。
更新
根据AmigoNico的评论,我提出了以下常量内存解决方案。 任何意见/改进都表示赞赏!
val writeAggr = (kv : (String, Int)) => {println(kv._1 + " " + kv._2)}
writeAggr(
( ("", 0) /: scala.io.Source.fromFile("/tmp/xx").getLines ) { (keyAggr, line) =>
val Array(k,v) = line split ' '
if (keyAggr._1.equals(k)) {
(k, keyAggr._2 + v.toInt)
} else {
if (!keyAggr._1.equals("")) {
writeAggr(keyAggr)
}
(k, v.toInt)
}
}
)
答案 0 :(得分:3)
这可以用Scalaz streams非常优雅地完成(与基于迭代器的解决方案不同,它“真正”有效):
import scalaz.stream._
val process =
io.linesR("input.txt")
.map { _.split("\\s") }
.map { case Array(k, v) => k -> v.toInt }
.pipe(process1.chunkBy2(_._1 == _._1))
.map { kvs => s"${ kvs.head._1 } ${ kvs.map(_._2).sum }\n" }
.pipe(text.utf8Encode)
.to(io.fileChunkW("output.txt"))
这不仅会从输入中读取,聚合行,还会在常量内存中写入输出,但您也可以获得有关资源管理的良好保证,例如: source.getLines
无法提供。
答案 1 :(得分:2)
你可能想要使用折叠,如下所示:
scala> ( ( Map[String,Int]() withDefaultValue 0 ) /: scala.io.Source.fromFile("/tmp/xx").getLines ) { (map,line) =>
val Array(k,v) = line split ' '
map + ( k -> ( map(k) + v.toInt ) )
}
res12: scala.collection.immutable.Map[String,Int] = Map(A -> 3, B -> 4)
折叠对于积累结果非常有用(与理解不同)。由于getLines
返回Iterator
,因此一次只能在内存中保留一行。
更新:好的,有一个新要求我们也没有将结果保存在内存中。在那种情况下,我想我只是写一个递归函数并像这样使用它:
scala> val kvPairs = scala.io.Source.fromFile("/tmp/xx").getLines map { line =>
val Array(k,v) = line split ' '
( k, v.toInt )
}
kvPairs: Iterator[(String, Int)] = non-empty iterator
scala> final def loop( key:String, soFar:Int ) {
if ( kvPairs.hasNext ) {
val (k,v) = kvPairs.next
if ( k == key )
loop( k, soFar+v )
else {
println( s"$key $soFar" )
loop(k,v)
}
} else println( s"$key $soFar" )
}
loop: (key: String, soFar: Int)Unit
scala> val (k,v) = kvPairs.next
k: String = A
v: Int = 1
scala> loop(k,v)
A 3
B 4
但唯一有用的是它使用递归函数而不是循环。如果您可以在内存中保存特定键的所有值,那么您可以编写一个迭代文件行的函数,生成类似键对的迭代器迭代器,您可以然后只是求和并打印,但代码仍然没有特别的功能,而且速度会慢一些。
Travis的Scalaz管道解决方案在这些方面看起来很有趣,但迭代隐藏在一些方便的构造之后。如果您特别想要一个功能性解决方案,我会说他是最好的答案。