我正在使用F#进行数据处理。首先,我将所有文件放在一个目录中,然后处理每个文件以生成一些数据结构。最后,我将处理后的数据存储到SQLite中。我知道如果我使用Seq存储文件名,然后管道转发到Seq.map,它将为每个文件执行延迟处理。但是如何在内存中包含所有这些文件的文件是不可能的。然后在命令式编程语言中,我可以读取一个文件,处理它,存储它并释放中间数据并执行下一个文件。当然F#可以做命令式编程,但我想知道在函数式编程风格中是否有机会这样做?
$addToSet
上面的代码显示了我的观点。 files
|> Seq.map readFile
|> Seq.map processContent
|> Seq.map storeProcessResult
包含一系列文件名,然后我读取文件的内容,将其处理成某种结构,最后将结果存储到数据库中。我知道由于惰性行为,文件将被逐个读取和处理。但最终数据什么时候发布?
答案 0 :(得分:1)
显然只有你知道readFile,processContent和storeProcessResult函数中发生了什么。正如@FuleSnabel在他的评论中所说,你可以映射然后使用fold(递归)来处理文件。
这是一个简单的测试,您可以执行以查看内存消耗的差异:创建一个包含1000万个元素的列表列表并对列表求和,然后创建一个包含1000万个元素的Seq列表,并对列表求和。我正在使用64位FSI。
这将使用大约1GB的内存:
let z = [for i in 1..3 -> List.init 10000000 (fun _ -> 1)]
let w = z |> List.map (fun x -> System.GC.Collect();List.sum x)
这只会使用几MB的内存,远远低于一个包含1000万1s的列表:
let x = seq {for i in 1..3 -> List.init 10000000 (fun _ -> 1 ) }
let y = x |> Seq.map (fun x -> System.GC.Collect(); List.sum x)
这只是工作流程中的一个(也可能是简单的)部分。如果你要打开文件,你必须确保关闭它们,因此我建议上面的use。但是我确实认识到访问文件系统并以延迟序列处理大量数据可能会导致一些问题,在这种情况下,您可以随时对其进行分析并查看瓶颈所在。
顺便说一句,您不需要直接在代码中调用GC,我只是这样做,中间结果不会污染测试中的内存计数。