What am I doing wrong in chunking a list in F#

不打扰是莪最后的温柔 提交于 2019-12-22 14:54:35

问题


F# doesn't come easy to me. The following piece of code is supposed to chunk a list. I have no idea what the problem is. Please help.

let chunk items chunkSize =
    let folder = fun state x ->
        match state with (reversedResult, reversedChunk) ->
            if reversedChunk.Length < chunkSize then
                (reversedResult, x::reversedChunk)
            else
                ((reversedChunk |> List.rev)::reversedResult, [x])
    let alsmostDone = items |> List.fold folder ([], [])
    match alsmostDone with
    | (reversedResult, []) -> reversedResult |> List.rev
    | (reversedResult, lastReversedChunk) -> (lastReversedChunk |> List.rev)::reversedResult |> List.rev


回答1:


I think using List.length is a bit more "idiomatic" f#. And then you don't need any type annotations. So:

...
if List.length reversedChunk < chunkSize then
...



回答2:


The type of reversedChunk can not be inferred so you need to specify a type. Change your third line to this:

match state with (reversedResult, reversedChunk : list<'T>) ->



回答3:


As others already mentioned, you can use List.length instead of Length to make this work. In practice, it might be better to keep the current chunk length as part of the state, because List.length needs to iterate over the entire list to calculate its length - so you will keep iterating over the chunk each time.

The following is pretty much the same as your original code, but I turned folder into an ordinary function (no need for lambda here) and I removed match (because you can pattern match on the state directly in the function declaration). Then I added reversedChunkSize to the state:

let chunk items chunkSize =
    let folder (reversedResult, reversedChunk, reversedChunkSize) x =
      if reversedChunkSize < chunkSize then
          (reversedResult, x::reversedChunk, reversedChunkSize + 1)
      else
          ((reversedChunk |> List.rev)::reversedResult, [x], 1)
    let alsmostDone = items |> List.fold folder ([], [], 0)
    match alsmostDone with
    | (reversedResult, [], _) -> 
         reversedResult |> List.rev
    | (reversedResult, lastReversedChunk, _) -> 
         (lastReversedChunk |> List.rev)::reversedResult |> List.rev



回答4:


This is more to address the first sentence of your question, than the particular coding problem.

Along with (re)inventing from scratch own algorithms FP language mastery is also facilitated by developing an idiomatic thinking of solutions in terms of standard core library functions and combinators. The task you were solving can be achieved by few straightforward idiomatic data transformations:

let chunk size items =                     // test upon chunk 3 [1..6]
    items                                  // [1;2;3;4;5;6]
    |> List.mapi (fun i x -> (i/size,x))   // [(0, 1); (0, 2); (0, 3); (1, 4); (1, 5); (1, 6)]
    |> Seq.groupBy fst                     // seq [(0, seq [(0, 1); (0, 2); (0, 3)]); (1, seq [(1, 4); (1, 5); (1, 6)])]
    |> Seq.map snd                         // seq [seq [(0, 1); (0, 2); (0, 3)]; seq [(1, 4); (1, 5); (1, 6)]]
    |> Seq.map (Seq.map snd >> Seq.toList) // seq [[1; 2; 3]; [4; 5; 6]]
    |> Seq.toList                          // [[1; 2; 3]; [4; 5; 6]]



回答5:


'GetSlice' version:

let chunk2 size items =
    [
        let arrs = items |> Seq.toArray
        let len = List.length items
        for i in [0..size..len-1] do
            let min = System.Math.Min(i+size, len)
            yield arrs.[i..min-1] |> Array.toList
    ]

let a = chunk2 6 [1..10000]


来源:https://stackoverflow.com/questions/18840705/what-am-i-doing-wrong-in-chunking-a-list-in-f

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!