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:
[1..17] |> batchesOf 5
Which would split the sequence into batches with a max of five in each, i.e:
[[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]; [11; 12; 13; 14; 15]; [16; 17]]
My first attempt at doing this is kind of ugly where I’ve resorted to using a mutable ref object after running into errors trying to use mutable type inside the closure. Using ref is particularly unpleasant since to dereference it you have to use the ! operator which when inside a condition expression can be counter intuitive to some devs who will read it as logical not. Another problem I ran into is where Seq.skip and Seq.take are not like their Linq aliases in that they will throw an error if size exceeds the size of the sequence.
let batchesOf size (sequence: _ seq) : _ list seq =
seq {
let s = ref sequence
while not (!s |> Seq.isEmpty) do
yield !s |> Seq.truncate size |> List.ofSeq
s := System.Linq.Enumerable.Skip(!s, size)
}
Anyway what would be the most elegant/idiomatic way to rewrite this in F#? Keeping the original behaviour but preferably without the ref mutable variable.
Implementing this function using the
seq<_>type idiomatically is difficult – the type is inherently mutable, so there is no simple nice functional way. Your version is quite inefficient, because it usesSkiprepeatedly on the sequence. A better imperative option would be to useGetEnumeratorand just iterate over elements usingIEnumerator. You can find various imperative options in this snippet: http://fssnip.net/1oIf you’re learning F#, then it is better to try writing the function using F# list type. This way, you can use idiomatic functional style. Then you can write
batchesOfusing pattern matching with recursion and accumulator argument like this:As a footnote, the imperative version can be made a bit nicer using computation expression for working with
IEnumerator, but that’s not standard and it is quite advanced trick (for example, see http://fssnip.net/37).