考虑以下用例:
我想并行迭代2个db表,并找到两个表中的差异和差距/缺失记录。假设1)pk表是一个Int ID字段; 2)按ID顺序读取表格; 3)任何一个表中都可能缺少记录(具有相应的序列缺口)。
我想在每个数据库的一次传递中执行此操作 - 使用延迟读取。 (我的这个程序的初始版本使用序列对象和数据阅读器 - 不幸的是,每个数据库都进行了多次传递。)
我想过使用成对序列处理并在迭代中使用Seq.skip来尝试保持表处理同步。然而,显然这是非常缓慢的,因为我Seq.skip有很高的开销(在引擎盖下创建新的序列)所以这可能是一个大表(例如200k recs)的问题。
我认为这是一种常见的设计模式(比较来自不同来源的并发数据流),并对类似项目的反馈/评论/链接感兴趣。
有人评论吗?
答案 0 :(得分:1)
使用序列时,任何延迟函数都会在序列上增加一些开销。在同一序列上调用Seq.skip数千次显然会很慢。
您可以使用Seq.zip
或Seq.map2
一次处理两个序列:
> Seq.map2 (+) [1..3] [10..12];;
val it : seq<int> = seq [11; 13; 15]
如果Seq模块不够用,您可能需要编写自己的功能。 我不确定我是否理解你尝试做什么,但这个示例函数可能对你有帮助:
let fct (s1: seq<_>) (s2: seq<_>) =
use e1 = s1.GetEnumerator()
use e2 = s2.GetEnumerator()
let rec walk () =
// do some stuff with the element of both sequences
printfn "%d %d" e1.Current e2.Current
if cond1 then // move in both sequences
if e1.MoveNext() && e2.MoveNext() then walk ()
else () // end of a sequence
elif cond2 then // move to the next element of s1
if e1.MoveNext() then walk()
else () // end of s1
elif cond3 then // move to the next element of s2
if e2.MoveNext() then walk ()
else () // end of s2
// we need at least one element in each sequence
if e1.MoveNext() && e2.MoveNext() then walk()
修改:
之前的功能是为了扩展Seq模块的功能,你可能想要使它成为一个高阶函数。正如ildjarn所说,使用LazyList可以使代码更清晰:
let rec merge (l1: LazyList<_>) (l2: LazyList<_>) =
match l1, l2 with
| LazyList.Cons(h1, t1), LazyList.Cons(h2, t2) ->
if h1 <= h2 then LazyList.cons h1 (merge t1 l2)
else LazyList.cons h2 (merge l1 t2)
| LazyList.Nil, l2 -> l2
| _ -> l1
merge (LazyList.ofSeq [1; 4; 5; 7]) (LazyList.ofSeq [1; 2; 3; 6; 8; 9])
但我仍然认为你应该将数据的迭代与处理分开。编写高阶函数进行迭代是一个好主意(最后,如果迭代器函数代码使用可变枚举器,那就不烦人了。)
答案 1 :(得分:1)
这是我(完全未经测试的)拍摄,对两个表进行一次传递:
let findDifferences readerA readerB =
let idsA, idsB =
let getIds (reader:System.Data.Common.DbDataReader) =
reader |> LazyList.unfold (fun reader ->
if reader.Read ()
then Some (reader.GetInt32 0, reader)
else None)
getIds readerA, getIds readerB
let onlyInA, onlyInB = ResizeArray<_>(), ResizeArray<_>()
let rec impl a b =
let inline handleOnlyInA idA as' = onlyInA.Add idA; impl as' b
let inline handleOnlyInB idB bs' = onlyInB.Add idB; impl a bs'
match a, b with
| LazyList.Cons (idA, as'), LazyList.Cons (idB, bs') ->
if idA < idB then handleOnlyInA idA as'
elif idA > idB then handleOnlyInB idB bs'
else impl as' bs'
| LazyList.Nil, LazyList.Nil -> () // termination condition
| LazyList.Cons (idA, as'), _ -> handleOnlyInA idA as'
| _, LazyList.Cons (idB, bs') -> handleOnlyInB idB bs'
impl idsA idsB
onlyInA.ToArray (), onlyInB.ToArray ()
这需要两个DataReader
s(每个表一个)并返回两个int[]
s,它们指示仅存在于其各自表中的ID。该代码假定ID字段的类型为int
,并且序号为0
。
另请注意,此代码使用F# PowerPack中的LazyList
,因此如果您还没有,则需要获取该代码。如果您的目标是.NET 4.0,那么我强烈建议您获取我已构建并托管的here的.NET 4.0二进制文件,因为来自F#PowerPack站点的二进制文件仅针对.NET 2.0,有时不会播放VS2010 SP1很好(有关详细信息,请参阅此主题:Problem with F# Powerpack. Method not found error)。