问题
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