Map

Here are two functions we might want to write:

(* add 1 to each element of list *)
let rec add1 = function
  | [] -> []
  | h::t -> (h+1)::(add1 t)

(* concatenate "3110" to each element of list *)
let rec concat3110 = function
  | [] -> []
  | h::t -> (h^"3110")::(concat3110 t)

When given input [a; b; c] they produce these results:

add1:       [a+1;      b+1;      c+1]
concat3110: [a^"3110"; b^"3110"; c^"3110"]

Let's introduce these definitions:

let f = fun x -> x+1
let g = fun x -> x^"3110"

Then we can rewrite the previous results as:

add1:       [f a; f b; f c]
concat3110: [g a; g b; g c]

Once again we notice some common structure that could be factored out. The only real difference between these two functions is that they apply a different function when transforming the head element.

So let's abstract that function from the definitions of add1 and concat3110, and make it an argument. Call the unified version of the two map, because it maps each element of the list through a function:

(* [map f [x1; x2; ...; xn]] is [f x1; f x2; ...; f xn] *)
let rec map f = function
  | [] -> []
  | h::t -> (f h)::(map f t)

Now we can implement our original two functions quite simply:

let add1 lst = map (fun x -> x+1) lst
let concat3110 lst = map (fun x -> x^"3110") lst

And we can even remove the lst everywhere it appears, relying on the fact that map f will return a function that expects an input list:

let add1 = map (fun x -> x+1) 
let concat3110 = map (fun x -> x^"3110")

It's worthwhile putting both those version of the functions, with and without lst, into the toplevel so that you can observe that the types do not change.

We have now successfully applied the Abstraction Principle: the common structure has been factored out. What's left clearly expresses the computation, at least to the reader who is familiar with map, in a way that the original versions do not as quickly make apparent.

The idea of map exists in many programming languages. It's called List.map in the OCaml standard library. Python 3.5 also has it:

>>> print(list(map(lambda x: x+1, [1,2,3])))
[2, 3, 4]

Java 8 recently added map too.

results matching ""

    No results matching ""