我碰巧正在开发一个用于处理时间序列数据的库,它具有执行此操作的功能 - 它实际上更通用一些,因为它返回DateTime * float option * float option
以表示一个系列在指定日期具有值的情况,但是另一个没有。
该函数假定这两个序列已经排序——这意味着它只需要遍历它们一次(对于未排序的序列,您需要进行多次迭代或构建一些临时表)。
另请注意,与您的示例相比,参数交换。你需要给它DateTime * float
。该函数不是特别好 - 它的工作原理IEnumerable
是它需要使用可变枚举器(通常是丑陋的命令式东西)。一般来说,模式匹配不能很好地与序列一起工作——你可以得到头部,但你不能得到尾部——因为那样效率很低。你可以为 F# 列表写一个更好的...
open System.Collections.Generic
let alignWithOrdering (seq1:seq<'T * 'TAddress>) (seq2:seq<'T * 'TAddress>) (comparer:IComparer<_>) = seq {
let withIndex seq = Seq.mapi (fun i v -> i, v) seq
use en1 = seq1.GetEnumerator()
use en2 = seq2.GetEnumerator()
let en1HasNext = ref (en1.MoveNext())
let en2HasNext = ref (en2.MoveNext())
let returnAll (en:IEnumerator<_>) hasNext f = seq {
if hasNext then
yield f en.Current
while en.MoveNext() do yield f en.Current }
let rec next () = seq {
if not en1HasNext.Value then yield! returnAll en2 en2HasNext.Value (fun (k, i) -> k, None, Some i)
elif not en2HasNext.Value then yield! returnAll en1 en1HasNext.Value (fun (k, i) -> k, Some i, None)
else
let en1Val, en2Val = fst en1.Current, fst en2.Current
let comparison = comparer.Compare(en1Val, en2Val)
if comparison = 0 then
yield en1Val, Some(snd en1.Current), Some(snd en2.Current)
en1HasNext := en1.MoveNext()
en2HasNext := en2.MoveNext()
yield! next()
elif comparison < 0 then
yield en1Val, Some(snd en1.Current), None
en1HasNext := en1.MoveNext()
yield! next ()
else
yield en2Val, None, Some(snd en2.Current)
en2HasNext := en2.MoveNext()
yield! next () }
yield! next () }
假设我们想使用字符串作为键(而不是你的DateTime
),你可以这样称呼它:
alignWithOrdering
[ ("b", 0); ("c", 1); ("d", 2) ]
[ ("a", 0); ("b", 1); ("c", 2) ] (Comparer<string>.Default) |> List.ofSeq
// Returns
[ ("a", None, Some 0); ("b", Some 0, Some 1);
("c", Some 1, Some 2); ("d", Some 2, None) ]
如果您对在 F# 中处理股票数据的时间序列感兴趣,您可能有兴趣加入F# Foundation的F# for Data and Machine Learning工作组。我们目前正在开发一个支持时间序列的开源库,这使得这变得更好:-)。如果您有兴趣查看并为早期预览做出贡献,那么您可以通过此工作组进行。