Take N elements from sequence with N different indexes in F#

后端 未结 4 1368
清酒与你
清酒与你 2021-01-18 01:50

I\'m new to F# and looking for a function which take N*indexes and a sequence and gives me N elements. If I have N indexes it should be equal to concat Seq.nth index0, Seq.n

4条回答
  •  孤独总比滥情好
    2021-01-18 02:29

    Is it a problem, that the returned result is sorted? This algorithm will work linearly over the input sequence. Just the indices need to be sorted. If the sequence is large, but indices are not so many - it'll be fast. Complexity is: N -> Max(indices), M -> count of indices: O(N + MlogM) in the worst case.

    let seqTakeIndices indexes = 
        let rec gather prev idxs xs =
            match idxs with
            | [] -> Seq.empty
            | n::ns ->  seq { let left = xs |> Seq.skip (n - prev)
                              yield left |> Seq.head
                              yield! gather n ns left }
        indexes |> List.sort |> gather 0
    

    Here is a List.fold variant, but is more complex to read. I prefer the first:

    let seqTakeIndices indices xs = 
        let gather (prev, xs, res) n =
            let left = xs |> Seq.skip (n - prev)
            n, left, (Seq.head left)::res
        let _, _, res = indices |> List.sort |> List.fold gather (0, xs, [])
        res
    

    Appended: Still slower than your variant, but a lot faster than mine older variants. Because of not using Seq.skip that is creating new enumerators and was slowing down things a lot.

    let seqTakeIndices indices (xs : seq<_>) = 
        let enum = xs.GetEnumerator()
        enum.MoveNext() |> ignore
        let rec gather prev idxs =  
            match idxs with
            | [] -> Seq.empty
            | n::ns -> seq { if [1..n-prev] |> List.forall (fun _ -> enum.MoveNext()) then 
                                yield enum.Current
                                yield! gather n ns }
        indices |> List.sort |> gather 0
    

提交回复
热议问题