So, let us say we have some list like as follows: [1; 2; 3; 4; 5; 6]
, and let us say that I want to fold on 2 elements per call of the function.
So, I would apply the function on (1, 2)
, (3, 4)
, and (5, 6)
in order.
Here was my attempt at a function to do so:
let fold_left_multiple (func: 'a -> 'b list -> 'a) (base: 'a) (lst: 'b list) (items_per_fold: int): 'a * 'b list =
let (acc, remainder, _) = List.fold_left (fun (acc, cur_fold_acc, cur_num) el ->
if cur_num mod items_per_fold = 0 then (func acc (List.rev (el::cur_fold_acc)), [], 1)
else (acc, el::cur_fold_acc, cur_num + 1)
) (base, [], 1) lst in (acc, remainder)
This somewhat works; however, the problem with this is that it is not easy to use these elements in a function.
My preferred implementation would somehow use a tuple or array to make element access easier.
Here is an example of input/output that would be better expected (using utop
syntax). In this case, I am summing up each pair of elements.
# fold_left_multiple (fun lst (e1, e2, e3) -> (e1 + e2 + e3)::lst) [] [1; 2; 3; 4; 5; 6; 7; 8] 3;;
- : int list * int list = ([15; 6], [7; 8])
Here, the remaining elements if the list's length is not evenly divisible by n
are put into the second element of the tuple.
(I would not mind if this remainder is reversed in a solution.)
You could just modify the standard fold_left
function to operate on multiple elements. Here's one that operates on pairs:
let rec fold_left_2 f acc l =
match l with
| a::b::rest -> fold_left_2 f (f acc a b) rest
| remainder -> (acc, remainder)
Edit: Modified to return the remainder, as asked, instead of ignoring it.
To illustrate my point in the comments that generalizing this to an arbitrary number of elements is possible, but not very beneficial, here's an implementation that allows the input list to be split arbitrarily using a split function:
let rec fold_left_n splitf f acc l =
match splitf l with
| None, remainder -> (acc, remainder)
| Some x, rest -> fold_left_n splitf f (f acc x) rest
And its invocation using your example:
fold_left_n
(function a::b::c::rest -> (Some (a, b, c), rest) | remainder -> (None, remainder))
(fun lst (e1, e2, e3) -> (e1 + e2 + e3)::lst) [] [1; 2; 3; 4; 5; 6; 7; 8];;
Similarly, a function could be written that extracts sublists of arbitrary length which I haven't bothered to implement, but its invocation would look something like this:
fold_left_n 3
(fun lst -> function
| [e1, e2, e3] -> (e1 + e2 + e3)::lst
| _ -> lst (* we assume we're getting a 3-element list, but the compiler doesn't know that so we need to ignore everything else *)
) [] [1; 2; 3; 4; 5; 6; 7; 8];;
They're both very complex and verbose in use and provide little benefit over just writing specialized implementations.