递归 F# 函数中的数组与列表
Array vs List in recursive F# function
我有这个功能:
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
它在列表上运行,但我想知道使用数组是否没有意义,因为数据最初是作为数组出现的,所以我必须将它转换为列表,然后按顺序遍历它,像一个数组。
但是如果我把它转换成一个数组,head/tail部分不是每次都重新创建数组吗?那么我应该保留整个数组并只使用索引进行迭代吗?
如果您想避免转换为 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
另一种方法是使用 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
类似,但会同时生成一个数组。
我有这个功能:
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
它在列表上运行,但我想知道使用数组是否没有意义,因为数据最初是作为数组出现的,所以我必须将它转换为列表,然后按顺序遍历它,像一个数组。
但是如果我把它转换成一个数组,head/tail部分不是每次都重新创建数组吗?那么我应该保留整个数组并只使用索引进行迭代吗?
如果您想避免转换为 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
另一种方法是使用 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
类似,但会同时生成一个数组。