计算移动平均线在F# [英] Calculating a moving average in F#
问题描述
我还在上groking F#的东西 - 试图找出如何去思考F#中,而不是从其他语言我只知道翻译
I'm still working on groking the F# thing - trying to work out how to 'think' in F# rather than just translating from other languages I know.
我最近一直在思考,你没有1例:前后1的地图。情况下,List.map倒下。
I've recently been thinking about the cases where you don't have a 1:1 map between before and after. Cases where List.map falls down.
这方面的一个例子是移动平均线,在那里通常你会在n个项目均在+ 1结果长度LEN列表len个-N。
One example of this is moving averages, where typically you will have len-n+1 results for a list of length len when averaging over n items.
对于大师在那里,这是一个很好的方式(使用队列从<一个捏做href="http://blogs.msdn.com/jomo_fisher/archive/2007/12/12/strange-confluence-an-immutable-queue-in-f.aspx"相对=nofollow>乔莫·费舍尔)?
For the gurus out there, is this a good way to do it (using queue pinched from Jomo Fisher)?
//Immutable queue, with added Length member
type Fifo<'a> =
new()={xs=[];rxs=[]}
new(xs,rxs)={xs=xs;rxs=rxs}
val xs: 'a list;
val rxs: 'a list;
static member Empty() = new Fifo<'a>()
member q.IsEmpty = (q.xs = []) && (q.rxs = [])
member q.Enqueue(x) = Fifo(q.xs,x::q.rxs)
member q.Length() = (List.length q.xs) + (List.length q.rxs)
member q.Take() =
if q.IsEmpty then failwith "fifo.Take: empty queue"
else match q.xs with
| [] -> (Fifo(List.rev q.rxs,[])).Take()
| y::ys -> (Fifo(ys, q.rxs)),y
//List module, add function to split one list into two parts (not safe if n > lst length)
module List =
let splitat n lst =
let rec loop acc n lst =
if List.length acc = n then
(List.rev acc, lst)
else
loop (List.hd lst :: acc) n (List.tl lst)
loop [] n lst
//Return list with moving average accross len elements of lst
let MovingAverage (len:int) (lst:float list) =
//ugly mean - including this in Fifo kills genericity
let qMean (q:Fifo<float>) = ((List.sum q.xs) + (List.sum q.rxs))/(float (q.Length()))
//get first part of list to initialise queue
let (init, rest) = List.splitat len lst
//initialise queue with first n items
let q = new Fifo<float>([], init)
//loop through input list, use fifo to push/pull values as they come
let rec loop (acc:float list) ls (q:Fifo<float>) =
match ls with
| [] -> List.rev acc
| h::t ->
let nq = q.Enqueue(h) //enqueue new value
let (nq, _) = nq.Take() //drop old value
loop ((qMean nq)::acc) t nq //tail recursion
loop [qMean q] rest q
//Example usage
MovingAverage 3 [1.;1.;1.;1.;1.;2.;2.;2.;2.;2.]
(可能是更好的方式是由先进先出继承来实现MovingAverageQueue?)
(Maybe a better way would be to implement a MovingAverageQueue by inheriting from Fifo?)
推荐答案
如果你没有太在意性能,这里是一个非常简单的解决办法:
If you don't care too much about performance, here is a very simple solution:
#light
let MovingAverage n s =
Seq.windowed n s
|> Seq.map Array.average
let avgs = MovingAverage 5000 (Seq.map float [|1..999999|])
for avg in avgs do
printfn "%f" avg
System.Console.ReadKey() |> ignore
该重新计算每个从头开始'窗口'的平均值,因此它是可怜的,如果窗户都大。
This recomputes the average of each 'window' from scratch, so it is poor if the windows are large.
在任何情况下,请Seq.windowed:
In any case, check out Seq.windowed:
<一个href="http://research.microsoft.com/projects/cambridge/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.Seq.html">http://research.microsoft.com/projects/cambridge/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.Seq.html
,因为它很方便的在你的后面的口袋里这样的事情。
as it's handy to have in your back pocket for such things.
这篇关于计算移动平均线在F#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!