【问题标题】:Array vs List in recursive F# function递归 F# 函数中的数组与列表
【发布时间】:2021-05-30 12:13:05
【问题描述】:

我有这个功能:

let calculate (candles: CandleData list) : decimal list =
    let inline range (q: CandleData) = q.High - q.Low
    let inline price (q: CandleData) = range q / 2m + q.Low

    let rec update (candles: CandleData list) (output: decimal list) lastPrice priceTracker rangeTracker stateTracker lambda =
        match candles with
        | [] -> output
        | h::t ->
            let priceTracker' = 0.2m * (price h - lastPrice) + 0.8m * priceTracker
            let rangeTracker' = 0.1m * (range h) + 0.8m * rangeTracker
            let lambda        = if rangeTracker <> 0m then abs(priceTracker / rangeTracker) else lambda
            let sqr           = decimal (sqrt (double (lambda * lambda * lambda * lambda + 16m * lambda * lambda)))
            let alpha         = (-lambda * lambda + sqr) / 8m
            let stateTracker' = alpha * price h + (1m - alpha) * stateTracker
            update t (output @ [decimal stateTracker']) (price h) priceTracker' rangeTracker' stateTracker' lambda

    update candles [] (price candles.Head) 0m (range candles.Head) (price candles.Head) 0.01m

它在列表上运行,但我想知道使用数组是否没有意义,因为数据最初是作为数组出现的,所以我必须将它转换为列表,然后按顺序遍历它,就像一个数组。

但是如果我将其转换为数组,头部/尾部不是每次都重新创建数组吗?那么我应该保持整个数组并只使用索引进行迭代吗?

【问题讨论】:

  • 事实上,update 函数的效率很低,因为它将新元素附加到输出 (output @ [...])。如果它在头部 ... :: output 添加元素然后反转列表会更有效。
  • 另外,随意使用带有可变变量的循环。只要可变值保存在一个简单的函数中并且不与代码的其他部分共享,这没有什么问题。

标签: arrays list optimization f#


【解决方案1】:

另一种方法是使用mapFold:

let calculate3 (candles : _[]) =
    let inline range (q: CandleData) = q.High - q.Low
    let inline price (q: CandleData) = range q / 2m + q.Low

    let init = price candles.[0], 0m, range candles.[0], price candles.[0], 0.01m
    (init, candles)
    ||> Array.mapFold (fun (lastPrice, priceTracker, rangeTracker, stateTracker, lambda) candle ->
            let priceTracker' = 0.2m * (price candle - lastPrice) + 0.8m * priceTracker
            let rangeTracker' = 0.1m * (range candle) + 0.8m * rangeTracker
            let lambda        = if rangeTracker <> 0m then abs(priceTracker / rangeTracker) else lambda
            let sqr           = decimal (sqrt (double (lambda * lambda * lambda * lambda + 16m * lambda * lambda)))
            let alpha         = (-lambda * lambda + sqr) / 8m
            let stateTracker' = alpha * price candle + (1m - alpha) * stateTracker
            stateTracker, (price candle, priceTracker', rangeTracker', stateTracker', lambda) )
    |> fst

它的工作方式类似于scan,但同时产生一个数组。

【讨论】:

  • 我从来没有用过mapFold,我会读一下;谢谢!
【解决方案2】:

如果您想避免转换为 list 的成本并仍将其保留为惯用的 F#,您可以 scan 原始数组而不是递归:

let calculate (candles : _[]) =
    let inline range (q: CandleData) = q.High - q.Low
    let inline price (q: CandleData) = range q / 2m + q.Low

    let init = price candles.[0], 0m, range candles.[0], price candles.[0], 0.01m
    (init, candles)
        ||> Seq.scan (fun (lastPrice, priceTracker, rangeTracker, stateTracker, lambda) candle ->
            let priceTracker' = 0.2m * (price candle - lastPrice) + 0.8m * priceTracker
            let rangeTracker' = 0.1m * (range candle) + 0.8m * rangeTracker
            let lambda        = if rangeTracker <> 0m then abs(priceTracker / rangeTracker) else lambda
            let sqr           = decimal (sqrt (double (lambda * lambda * lambda * lambda + 16m * lambda * lambda)))
            let alpha         = (-lambda * lambda + sqr) / 8m
            let stateTracker' = alpha * price candle + (1m - alpha) * stateTracker
            price candle, priceTracker', rangeTracker', stateTracker', lambda)
        |> Seq.map (fun (lastPrice, priceTracker, rangeTracker, stateTracker, lambda) ->
            stateTracker)
        |> Seq.skip 1
        |> Seq.toArray

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-12
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    • 2012-01-15
    • 2014-10-18
    相关资源
    最近更新 更多