f# - 还有其他惯用的方式来传递状态信息吗?

标签 f#

我需要处理一系列毫秒时间范围的历史报价数据。需要过滤某些时间跨度(每小时、每分钟等)的开盘报价的能力。序列可能有比跨度更大的间隙,因此必须选择该间隙之后的第一个刻度作为开始刻度,否则开始刻度是最接近对应时间跨度的日历开始 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/

相关文章:

.net - 如何获取数组中元素的大小?

inheritance - F#:将接口(interface)(如 IComparable)添加到现有类型(例如,来自 Fare 等库)

c# - 在控制台应用程序中等待异步操作结束时,如何避免出现 Thread.Sleep(Int32.MaxValue)?

arrays - 为什么在 F# 中处理数组比列表更快

F# 静态成员

visual-studio - F# docker 应用程序 : A function labeled with the 'EntryPointAttribute' attribute must be the last declaration in the last file. ...?

performance - 为什么构建/拼接 F# 报价这么慢?

f# - 无法在F#签名文件(.fsi)中使用Literal属性

python - 相当于 Python 中 F# 的 Seq.scan() 方法?

f# - 如何在 F# 中定义这种惰性(无限?)数据结构