Fold Left

Given that there's a fold function that works from right to left, it stands to reason that there's one that works from left to right. That function is called List.fold_left in the OCaml library. Here is its implementation:

let rec fold_left op acc = function
  | []   -> acc
  | h :: t -> fold_left op (op acc h) t

The idea is that fold_left (+) 0 [a;b;c] results in evaluation of ((0+a)+b)+c. The parentheses associate from the left-most subexpression to the right. So fold_left is "folding in" elements of the list from the left to the right, combining each new element using the operator.

This function therefore works a little differently than fold_right. As a simple difference, notice that the list argument is the ultimate argument rather than penultimate.

More importantly, the name of the initial value argument has changed from init to acc, because it's no longer going to just be the initial value. The reason we call it acc is that we think of it as an accumulator, which is the result of combining list elements so far. In fold_right, you will notice that the value passed as the init argument is the same for every recursive invocation of fold_right: it's passed all the way down to where it's needed, at the right-most element of the list, then used there exactly once. But in fold_left, you will notice that at each recursive invocation, the value passed as the argument acc can be different.

For example, if we want to walk across a list of integers and sum them, we could store the current sum in the accumulator. We start with the accumulator set to 0. As we come across each new element, we add the element to the accumulator. When we reach the end, we return the value stored in the accumulator.

let rec sum' acc = function
  | []   -> acc
  | h :: t -> sum' (acc + h) t

let sum = sum' 0

Our fold_left function abstracts from the particular operator used in the sum'.

Using fold_left, we can rewrite sum and concat as follows:

let sum = List.fold_left (+) 0  
let concat = List.fold_left (^) ""

We have once more succeeded in applying the Abstraction Principle.

Here is the actual code from the standard library that implements the two fold functions:

let rec fold_left f accu l =
  match l with
    [] -> accu
  | a::l -> fold_left f (f accu a) l

let rec fold_right f l accu =
  match l with
    [] -> accu
  | a::l -> f a (fold_right f l accu)

The library calls the operator (or combining function) f instead of op, and the initial value for fold_right it calls accu by analogy to fold_left's accumulator, even though it's not truly an accumulator for fold_right.

results matching ""

    No results matching ""