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

后端 未结 8 1980
执念已碎
执念已碎 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条回答
  • 2021-01-02 06:31

    Okay, trying again. Achieving the optimal amount of laziness turns out to be a bit difficult in F#... On the bright side, this is somewhat more functional than my last attempt, in that it doesn't use any ref cells.

    let groupBy cmp (sq:seq<_>) =
      let en = sq.GetEnumerator()
      let next() = if en.MoveNext() then Some en.Current else None
      (* this function returns a pair containing the first sequence and a lazy option indicating the first element in the next sequence (if any) *)
      let rec seqStartingWith start =
        match next() with
        | Some y when cmp start y ->
            let rest_next = lazy seqStartingWith y // delay evaluation until forced - stores the rest of this sequence and the start of the next one as a pair
            seq { yield start; yield! fst (Lazy.force rest_next) }, 
              lazy Lazy.force (snd (Lazy.force rest_next))
        | next -> seq { yield start }, lazy next
      let rec iter start =
        seq {
          match (Lazy.force start) with
          | None -> ()
          | Some start -> 
              let (first,next) = seqStartingWith start
              yield first
              yield! iter next
        }
      Seq.cache (iter (lazy next()))
    
    0 讨论(0)
  • 2021-01-02 06:36

    Ok, here's an answer I'm not unhappy with.

    (EDIT: I am unhappy - it's wrong! No time to try to fix right now though.)

    It uses a bit of imperative state, but it is not too difficult to follow (provided you recall that '!' is the F# dereference operator, and not 'not'). It is as lazy as possible, and takes a seq as input and returns a seq of seqs as output.

    let N = 20
    let data =  // produce some arbitrary data with holes
        seq {
            for x in 1..N do
                if x % 4 <> 0 && x % 7 <> 0 then
                    printfn "producing %d" x
                    yield x
        }
    let rec GroupBy comp (input:seq<_>) = seq {
        let doneWithThisGroup = ref false
        let areMore = ref true
        use e = input.GetEnumerator()
        let Next() = areMore := e.MoveNext(); !areMore
        // deal with length 0 or 1, seed 'prev'
        if not(e.MoveNext()) then () else
        let prev = ref e.Current
        while !areMore do
            yield seq {
                while not(!doneWithThisGroup) do
                    if Next() then
                        let next = e.Current 
                        doneWithThisGroup := not(comp !prev next)
                        yield !prev 
                        prev := next
                    else
                        // end of list, yield final value
                        yield !prev
                        doneWithThisGroup := true } 
            doneWithThisGroup := false }
    let result = data |> GroupBy (fun x y -> y = x + 1)
    printfn "Consuming..."
    for group in result do
        printfn "about to do a group"
        for x in group do
            printfn "  %d" x
    
    0 讨论(0)
提交回复
热议问题