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

freeliver54 阅读:41 2024-06-03 14:00:57 评论:0

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

除此之外,我不完全确定您的其他纯解决方案 - 我认为它与第一个解决方案不完全相同(但我没有要测试的数据),因为您只是比较两个相邻的元素(所以,也许,在第一个元素中,您可以跳过多个项目?)


标签:程序员
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号