Kvb's answer may not give you what you want. Seq.windowed returns a sliding window of values, for example, [1; 2; 3; 4] [1; 2; 3; 4] [1; 2; 3; 4] becomes [[1; 2; 3]; [2; 3; 4]] [[1; 2; 3]; [2; 3; 4]] [[1; 2; 3]; [2; 3; 4]] . It looks like you want it to split into adjacent pieces. The following function takes a list and returns a list of triples ( 'T list -> ('T * 'T * 'T) list ).
let toTriples list = let rec aux f = function | a :: b :: c :: rest -> aux (fun acc -> f ((a, b, c) :: acc)) rest | _ -> f [] aux id list
Here's the opposite:
let ofTriples triples = let rec aux f = function | (a, b, c) :: rest -> aux (fun acc -> f (a :: b :: c :: acc)) rest | [] -> f [] aux id triples
EDIT
If you are dealing with huge amounts of data, here is a consistent approach with constant memory (all the created option and tuple negatively affect the GC-lower for the best version):
let (|Next|_|) (e:IEnumerator<_>) = if e.MoveNext() then Some e.Current else None let (|Triple|_|) = function | Next a & Next b & Next c -> Some (a, b, c) //change to [|a;b;c|] if you like | _ -> None let toSeqTriples (items:seq<_>) = use e = items.GetEnumerator() let rec loop() = seq { match e with | Triple (a, b, c) -> yield a, b, c yield! loop() | _ -> () } loop()
EDIT 2
The ebb memory usage prompted me to check, and I found that toSeqTriples is slow and causes unexpectedly frequent GCs. The next version fixes these problems and is almost 4 times faster than the list-based version.
let toSeqTriplesFast (items:seq<_>) = use e = items.GetEnumerator() let rec loop() = seq { if e.MoveNext() then let a = e.Current if e.MoveNext() then let b = e.Current if e.MoveNext() then let c = e.Current yield (a, b, c) yield! loop() } loop()
This is a relatively constant use of memory compared to a list or array based method, because: a) if you have seq to start with the whole sequence, you do not need to put in a list / array; and b) it also returns the sequence, making it lazy and avoiding allocating another list / array.
Daniel
source share