我需要处理一系列毫秒时间范围的历史报价数据。需要过滤某些时间跨度(每小时、每分钟等)的开盘报价的能力。序列可能有比跨度更大的间隙,因此必须选择该间隙之后的第一个刻度作为开始刻度,否则开始刻度是最接近对应时间跨度的日历开始 channel 的刻度。
首先想到的是下面的stateful过滤函数opensTimespan:Timespan->(Timestamp->bool)
,它捕获了每个空位的timespanId或在调用之间传递的闭包中打开间隔打勾:
let opensTimespan (interval: Timespan)=
let lastTakenId = ref -1L // Timestamps are positive
fun (tickAt: Timestamp) ->
let tickId = tickAt / interval in
if tickId <> !lastTakenId then lastTakenId := tickId; true
else false
并且可以这样应用:
let hourlyTicks = readTicks @"EURUSD-history.zip" "EURUSD-2012-04.csv"
|> Seq.filter (opensTimespan HOUR) |> Seq.toList
这很好用,但是具有副作用的 opensTimespan
绝对不是惯用的。
另一种选择可能是利用这一事实,即决定是否打开一个刻度,只需要自己的一对时间戳和前一个时间戳就可以得出以下无状态过滤功能opensTimespanF:Timespan->Timestamp*Timestamp->bool
:
let opensTimespanF interval (ticksPair: Timestamp*Timestamp) =
fst ticksPair/ interval <> snd ticksPair/ interval
可以应用为:
let hourlyTicks=
seq {
yield 0L;
yield! readTicks @"EURUSD-history.zip" "EURUSD-2012-04.csv"
}
|> Seq.pairwise |> Seq.filter (opensTimespanF HOUR)
|> Seq.map snd
|> Seq.toList
这种纯函数式方法产生的结果相当,但性能损失很小 (~11%)。
我可能会遗漏哪些以纯功能方式处理此任务的其他方式?
谢谢。
最佳答案
一个纯粹的函数式解决方案是使用 fold
函数。 fold
函数用于处理序列(或列表)并累积一些状态。在您的示例中,状态是 lastTakenId
以及您要返回的元素列表,因此您可以使用 Timestamp * (Timestamp list)
类型的状态:
let hourlyTicks =
readTicks @"EURUSD-history.zip" "EURUSD-2012-04.csv"
|> Seq.fold (fun (lastTakenId, res) tickAt ->
// Similar to the body of your stateful function - 'lastTakenId' is the last
// state and 'tickAt' is the current value. The 'res' list stores
// all returned elements
let tickId = tickAt / HOUR
if tickId <> lastTakenId then
// We return new state for 'lastTakenId' and append current element to result
(tickId, tickAt::res)
else
// Here, we skip element, so we return the original state and original list
(lastTakenId, res) ) (-1L, []) // Initial state: -1 and empty list of results
// Take the second part of the state (the result list) and
// reverse it, because it was accumulated in the opposite order
|> snd |> List.rev
除此之外,我不完全确定您的其他纯解决方案 - 我认为它与第一个解决方案不完全相同(但我没有要测试的数据),因为您只是比较两个相邻的元素(所以,也许,在第一个元素中,您可以跳过多个项目?)
关于f# - 还有其他惯用的方式来传递状态信息吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10704993/