Most idiomatic way to write batchesOf size seq in F#

前端 未结 10 504
迷失自我
迷失自我 2020-12-16 18:42

I\'m trying to learn F# by rewriting some C# algorithms I have into idiomatic F#.

One of the first functions I\'m trying to rewrite is a batchesOf where:

         


        
相关标签:
10条回答
  • 2020-12-16 18:55

    This can be done without recursion if you want

    [0..20] 
        |> Seq.mapi (fun i elem -> (i/size),elem) 
        |> Seq.groupBy (fun (a,_) -> a)
        |> Seq.map (fun (_,se) -> se |> Seq.map (snd));;
    val it : seq<seq<int>> =
      seq
        [seq [0; 1; 2; 3; ...]; seq [5; 6; 7; 8; ...]; seq [10; 11; 12; 13; ...];
         seq [15; 16; 17; 18; ...]; ...]
    

    Depending on how you think this may be easier to understand. Tomas' solution is probably more idiomatic F# though

    0 讨论(0)
  • 2020-12-16 18:57

    I found this to be a quite terse solution:

    let partition n (stream:seq<_>) = seq {
        let enum = stream.GetEnumerator()
    
        let rec collect n partition =
            if n = 1 || not (enum.MoveNext()) then
                partition
            else
                collect (n-1) (partition @ [enum.Current])
    
        while enum.MoveNext() do
            yield collect n [enum.Current]
    }
    

    It works on a sequence and produces a sequence. The output sequence consists of lists of n elements from the input sequence.

    0 讨论(0)
  • 2020-12-16 18:59

    You can solve your task with analog of Clojure partition library function below:

    let partition n step coll =
        let rec split ss =
            seq {
                yield(ss |> Seq.truncate n)
                if Seq.length(ss |> Seq.truncate (step+1)) > step then
                    yield! split <| (ss |> Seq.skip step)
                }
        split coll
    

    Being used as partition 5 5 it will provide you with sought batchesOf 5 functionality:

    [1..17] |> partition 5 5;;
    val it : seq<seq<int>> =
      seq
        [seq [1; 2; 3; 4; ...]; seq [6; 7; 8; 9; ...]; seq [11; 12; 13; 14; ...];
         seq [16; 17]]
    

    As a premium by playing with n and step you can use it for slicing overlapping batches aka sliding windows, and even apply to infinite sequences, like below:

    Seq.initInfinite(fun x -> x) |> partition 4 1;;
    val it : seq<seq<int>> =
      seq
        [seq [0; 1; 2; 3]; seq [1; 2; 3; 4]; seq [2; 3; 4; 5]; seq [3; 4; 5; 6];
         ...]
    

    Consider it as a prototype only as it does many redundant evaluations on the source sequence and not likely fit for production purposes.

    0 讨论(0)
  • 2020-12-16 19:05

    Here's a simple implementation for sequences:

    let chunks size (items:seq<_>) =
      use e = items.GetEnumerator()
      let rec loop i acc =
        seq {
          if i = size then 
            yield (List.rev acc)
            yield! loop 0 []
          elif e.MoveNext() then
            yield! loop (i+1) (e.Current::acc)
          else
            yield (List.rev acc)
        }
      if size = 0 then invalidArg "size" "must be greater than zero"
      if Seq.isEmpty items then Seq.empty else loop 0 []
    
    let s = Seq.init 10 id
    chunks 3 s 
    //output: seq [[0; 1; 2]; [3; 4; 5]; [6; 7; 8]; [9]]
    
    0 讨论(0)
提交回复
热议问题