Home > Software design >  How to structure an accumulator for fold_left with index access
How to structure an accumulator for fold_left with index access

Time:12-12

I want to write a function that takes a List [a_n; a_n-1; ...; a_0] with an accumulator acc.

The function is supposed to calculate the sum of every element in the whole list raised to the i'th power. The function fold_left will give f an integer. The formula is acc sum from i=0 to n of a_i ^ i. My problem is that in fold_left:

let fold_left f acc l = 
 match l with 
 | [] -> acc
 | x::xs -> fold_left f (f x acc) xs 

the accumulator always returns one integer -- so there's no reference for me to know what number the i'th element is.


So my question is how should I structure my f function.

f should be structured like this:

 f a_0 (f a_1 (...(f a_n acc)...))

I tried an imperative approach by using a ref variable that stores the previous values that f has calculated thus far. But I'm sure there are better solutions to this problem...

CodePudding user response:

The accumulator don't need to be an integer, it can be a tuple, or a record

type 'a acc = { pos:int; acc:'a }

CodePudding user response:

Let's consider a really imperative solution to your problem first.

A utility function so we can do integer exponentiation. We'll use List.iter to run an imperative loop over an int list.

let rec pow x =
  function
  | 0 -> 1
  | 1 -> x
  | n -> x * pow x (n - 1)
let lst = [1; 4; 7; 2]

let pos = ref 0
let sum = ref 0

let () = List.iter (fun x -> sum := !sum   pow x !pos; pos := !pos   1) lst

Printf.printf "The sum is %d\n" !sum

There were two pieces of information we needed to keep track of across the iteration: pos and sum. In this imperative style, we kept track of them by mutating a value.

When we use List.fold_left and use an accumulator to contain this same state, we no longer need to mutate any values.

let (pos, sum) = List.fold_left (fun (p, s) x -> (p   1, s   pow x p)) (0, 0) lst

You can see how the initial state provided mirrors the zeroes I provided in the imperative example.

If we look at how this progresses as it works over the list it's possible to get a better picture of how the accumulator tuple works.

List.fold_left f (0, 0) [1; 4; 7; 2]
List.fold_left f (1,  0   pow 1 0) [4; 7; 2]
List.fold_left f (2,  1   pow 4 1) [7; 2]
List.fold_left f (3,  5   pow 7 2) [2]
List.fold_left f (4, 54   pow 2 3) []
(4, 62)

CodePudding user response:

List.fold_left can accept an accumulator of any type

(* given some pow function *)
let rec pow a b =
  match b with
  | 0 -> 1
  | _ -> a * pow a (b - 1)

(* your initial accumulator *)
let init = (0, 0) in 

(* your folding function *)
let f (i, r) v = 
  (i   1, r   pow v i) in

(* note the output is a tuple of the last i and the sum *) 
let (i, result) = List.fold_left f init [10;20;30;40] in

(* print the result *)
Format.printf "result: %d\n" result
result: 64921

Check your work -

10^0   20^1   30^2   40^3 = 64921 ✅

CodePudding user response:

You can use a pair for the accumulator:

# let f (s, i) x =
      (s   pow x i, i   1);;
val f : int * int -> int -> int * int = <fun>

# let sum_powers_left l = fst (List.fold_left f (0, 0) l);;
val sum_powers_left : int list -> int = <fun>


# let g x (s, i) =
      (s   pow x i, i   1);;
val g : int -> int * int -> int * int = <fun>

# let sum_powers_right l = fst (List.fold_right g l (0, 0));;
val sum_powers_right : int list -> int = <fun>


# sum_powers_left [2000; 100; 20; 3];;   (* 1   100   20*20   3*3*3 *)
- : int = 528

# sum_powers_right [2000; 100; 20; 3];;  (* 2000*2000*2000   100*100   20   1 *)
- : int = 8000010021

where pow x i evaluates to x to the power of i. Sadly it's not in the standard library.

  • Related