F#: How do i split up a sequence into a sequence of sequences

后端 未结 8 2019
执念已碎
执念已碎 2021-01-02 06:12

Background:

I have a sequence of contiguous, time-stamped data. The data-sequence has gaps in it where the data is not contiguous. I want create a

8条回答
  •  猫巷女王i
    2021-01-02 06:23

    Below is some code that does what I think you want. It is not idiomatic F#.

    (It may be similar to Brian's answer, though I can't tell because I'm not familiar with the LazyList semantics.)

    But it doesn't exactly match your test specification: Seq.length enumerates its entire input. Your "test code" calls Seq.length and then calls Seq.hd. That will generate an enumerator twice, and since there is no caching, things get messed up. I'm not sure if there is any clean way to allow multiple enumerators without caching. Frankly, seq> may not be the best data structure for this problem.

    Anyway, here's the code:

    type State<'a> = Unstarted | InnerOkay of 'a | NeedNewInner of 'a | Finished
    
    // f() = true means the neighbors should be kept together
    // f() = false means they should be split
    let split_up (f : 'a -> 'a -> bool) (input : seq<'a>) =
        // simple unfold that assumes f captured a mutable variable
        let iter f = Seq.unfold (fun _ -> 
            match f() with
            | Some(x) -> Some(x,())
            | None -> None) ()
    
        seq {
            let state = ref (Unstarted)
            use ie = input.GetEnumerator()
    
            let innerMoveNext() = 
                match !state with
                | Unstarted -> 
                    if ie.MoveNext()
                    then let cur = ie.Current
                         state := InnerOkay(cur); Some(cur)
                    else state := Finished; None 
                | InnerOkay(last) ->
                    if ie.MoveNext()
                    then let cur = ie.Current
                         if f last cur
                         then state := InnerOkay(cur); Some(cur)
                         else state := NeedNewInner(cur); None
                    else state := Finished; None
                | NeedNewInner(last) -> state := InnerOkay(last); Some(last)
                | Finished -> None 
    
            let outerMoveNext() =
                match !state with
                | Unstarted | NeedNewInner(_) -> Some(iter innerMoveNext)
                | InnerOkay(_) -> failwith "Move to next inner seq when current is active: undefined behavior."
                | Finished -> None
    
            yield! iter outerMoveNext }
    
    
    open System
    
    let groupContigs (contigTime : TimeSpan) (holey : seq) =
        split_up (fun (t1,_) (t2,_) -> (t2 - t1) <= contigTime) holey
    
    
    // Test data
    let numbers = {1 .. 15}
    let contiguousTimeStamps = 
        let baseTime = DateTime.Now
        seq { for n in numbers -> baseTime.AddMinutes(float n)}
    
    let holeyData = 
        Seq.zip contiguousTimeStamps numbers 
            |> Seq.filter (fun (dateTime, num) -> num % 7 <> 0)
    
    let grouped_data = groupContigs (new TimeSpan(0,1,0)) holeyData
    
    
    printfn "Consuming..."
    for group in grouped_data do
        printfn "about to do a group"
        for x in group do
            printfn "  %A" x
    

提交回复
热议问题