开发者

f# sequence of running total

开发者 https://www.devze.com 2023-02-21 03:54 出处:网络
Ok, this looks like it should be easy, but I\'m just not getting it. If I have a sequence of numbers, how do I generate a new sequence made up of the running totals? eg for a sequence [1;2;3;4], I wan

Ok, this looks like it should be easy, but I'm just not getting it. If I have a sequence of numbers, how do I generate a new sequence made up of the running totals? eg for a sequence [1;2;3;4], I want to map it to [1;3;6;10]. In a suitably functional 开发者_JAVA百科way.


Use List.scan:

let runningTotal = List.scan (+) 0 >> List.tail

[1; 2; 3; 4]
|> runningTotal
|> printfn "%A"

Seq.scan-based implementation:

let runningTotal seq' = (Seq.head seq', Seq.skip 1 seq') ||> Seq.scan (+)

{ 1..4 }
|> runningTotal
|> printfn "%A"


Another variation using Seq.scan (Seq.skip 1 gets rid of the leading zero):

> {1..4} |> Seq.scan (+) 0 |> Seq.skip 1;;
val it : seq<int> = seq [1; 3; 6; 10]


> Seq.scan (fun acc n -> acc + n) 0 [1;2;3;4];;
val it : seq<int> = seq [0; 1; 3; 6; ...]

With lists:

> [1;2;3;4] |> List.scan (fun acc n -> acc + n) 0 |> List.tail;;
val it : int list = [1; 3; 6; 10]

Edit: Another way with sequences:

let sum s = seq {
    let x = ref 0
    for i in s do
        x := !x + i
        yield !x
}

Yes, there's a mutable variable, but I find it more readable (if you want to get rid of the leading 0).


Figured it was worthwhile to share how to do this with Record Types in case that's also what you came here looking for.

Below is a fictitious example demonstrating the concept using runner laps around a track.

type Split = double
type Lap = { Num : int; Split : Split }
type RunnerLap = { Lap : Lap; TotalTime : double }

let lap1 = { Num = 1; Split = 1.23 } 
let lap2 = { Num = 2; Split = 1.13 } 
let lap3 = { Num = 3; Split = 1.03 } 
let laps = [lap1;lap2;lap3]

let runnerLapsAccumulator =  
  Seq.scan 
    (fun rl l -> { rl with Lap = l; TotalTime = rl.TotalTime + l.Split }) // acumulator
    { Lap = { Num = 0; Split = 0.0 }; TotalTime = 0.0 } // initial state

let runnerLaps = laps |> runnerLapsAccumulator
printfn "%A" runnerLaps


Not sure this is the best way but it should do the trick

  let input = [1; 2; 3; 4]
  let runningTotal = 
    (input, 0) 
    |> Seq.unfold (fun (list, total) ->
      match list with
      | [] -> 
        None
      | h::t -> 
        let total = total + h
        total, (t, total) |> Some)
    |> List.ofSeq
0

精彩评论

暂无评论...
验证码 换一张
取 消