我试图找出内存效率和功能的方式来处理大规模使用字符串中阶数据处理大数据阶的功能性的方式。我已经阅读了很多关于懒惰集合的东西,并且看到了很多代码示例。但是,我一次又一次遇到“超出GC开销”或“Java堆空间”问题。斯卡拉懒收藏
通常的问题是,我试图建立一个懒惰的集合,但是当我把它添加到集种植(我不知道现在任何其他方式这样做增量)评估每一个新的元素。当然,我可以尝试一些方法,例如先初始化一个初始的懒惰集合,然后通过使用地图等应用资源关键型计算来生成具有所需值的集合,但通常我只是不知道最终集合的确切大小最初是懒惰的收集。
也许你可以通过给我提示或解释如何改进下面的代码来帮助我,例如根据奇数序列对属于一个的规则将FASTA(定义如下)格式的文件分割成两个单独的文件档案,甚至一个到另一个(“分离股”)。要做到这一点“最直接的”最简单的方法就是通过循环遍历行并通过打开的文件流打印到相应的文件中(当然这很好)。然而,我只是不喜欢重新赋值给包含头文件和序列的变量的样式,因此下面的示例代码使用(尾)递归,并且我希望找到一种方法来维护类似的设计而不会遇到资源问题!
该示例对于小文件非常适用,但已经在大约500mb左右的文件中,代码将因标准JVM设置而失败。我想处理“仲裁”大小的文件,比如10-20gb左右。引起我
val fileName = args(0)
val in = io.Source.fromFile(fileName) getLines
type itType = Iterator[String]
type sType = Stream[(String, String)]
def getFullSeqs(ite: itType) = {
//val metaChar = ">"
val HeadPatt = "(^>)(.+)" r
val SeqPatt = "([\\w\\W]+)" r
@annotation.tailrec
def rec(it: itType, out: sType = Stream[(String, String)]()): sType =
if (it hasNext) it next match {
case HeadPatt(_,header) =>
// introduce new header-sequence pair
rec(it, (header, "") #:: out)
case SeqPatt(seq) =>
val oldVal = out head
// concat subsequences
val newStream = (oldVal._1, oldVal._2 + seq) #:: out.tail
rec(it, newStream)
case _ =>
println("something went wrong my friend, oh oh oh!"); Stream[(String, String)]()
} else out
rec(ite)
}
def printStrands(seqs: sType) {
import java.io.PrintWriter
import java.io.File
def printStrand(seqse: sType, strand: Int) {
// only use sequences of one strand
val indices = List.tabulate(seqs.size/2)(_*2 + strand - 1).view
val p = new PrintWriter(new File(fileName + "." + strand))
indices foreach { i =>
p.print(">" + seqse(i)._1 + "\n" + seqse(i)._2 + "\n")
}; p.close
println("Done bro!")
}
List(1,2).par foreach (s => printStrand(seqs, s))
}
printStrands(getFullSeqs(in))
三个问题:
A)让我们假设一个需要保持通过处理从getLines
得到像我getFullSeqs
方法的初始迭代器获得一个大的数据结构(注意不同大小in
和getFullSeqs
输出),因为整体上的转换(!)数据被反复要求,因为一个不知道人会要求在任何步骤将数据的一部分。我的例子可能不是最好的,但怎么做呢?有没有可能? B)当期望的数据结构不是固有的懒惰时,例如,如果想将(header -> sequence)
对存储到Map()
中,什么时候?你会把它包装在一个懒惰的集合?
C)我构建流的实现可能会颠倒输入行的顺序。当调用reverse时,所有元素都将被评估(在我的代码中,它们已经是,所以这是实际的问题)。有没有什么办法可以从后面以懒惰的方式进行后期处理?我知道reverseIterator
,但这是否已经是解决方案,还是不会实际首先评估所有元素(因为我需要在列表中调用它)?我们可以用newVal #:: rec(...)
来构建这个流,但是我会失去尾递归,不是吗?
所以我基本上需要的是添加元素到一个集合,这是不加入评估的过程。所以lazy val elem = "test"; elem :: lazyCollection
是不是我所期待的。
编辑:我也尝试使用rec
的流参数的名称参数。
非常感谢您的关注和时间,我非常感谢您的帮助(再次:))。
////////////////////////////////////////////// ////////////////////////////////////////////////// ////////////////////////////////////////////////// ///////////////////
FASTA定义为一个顺序集合由单个头部行分隔序列。标题被定义为以“>”开头的行。标题下的每一行都被称为与标题相关的序列的一部分。一个序列在新的标题存在时结束。每个标题都是唯一的。例如:
>头1
ABCDEFG
> HEADER2
hijklmn
opqrstu
> HEADER3
VWXYZ
> HEADER4
zyxwv
因此,序列2是两倍大,SEQ 1我的计划是将文件分割成包含一个文件
>头1
ABCDEFG
> HEADER3
VWXYZ
和含有
> HEADER2
hijklmn
opqrstu第二文件B
> HEADER4
zyxwv
输入文件假定由偶数个头部序列组成配对。
http://pastebin.com/bvaKiA3e,做一模一样的必要和非常简洁的方式 - 只是它也可以与文件大小不受限制(然而,这仅仅是在特定的示例中的解决方案,但只要我需要改造整个数据,我会到相同的内存问题) –