csci2041/public-class-repo/SamplePrograms/Sec_10_3:35pm/tail.ml

196 lines
3.5 KiB
OCaml
Raw Normal View History

2018-01-29 23:35:31 +00:00
(* Some functions used in S4.3 Improving Performance
Some of this material comes from section 9.4 of Chris Reade's book
``Elements of Functional Programming''. This is in the `Resources`
directory of the public class repository.
Some were written by Charlie Harper.
*)
let rec listof n =
match n with
| 0 -> []
| _ -> n :: listof (n-1)
let rec append l1 l2 =
match l1 with
| [] -> l2
| x::xs -> x :: (append xs l2)
(* Problem 1: what is wrong with this function? *)
let rec rev lst =
match lst with
| [] -> []
| x::xs -> append (rev xs) [x]
(*- It runs in quadratic time. *)
(* Problem 2: how can these be improved? *)
let rec length lst = match lst with
| [] -> 0
| _::xs -> 1 + length xs
let rec sumlist lst = match lst with
| [] -> 0
| x::xs -> x + sumlist xs
(*- stacking up the additions
The evaluation looks like this:
sumlist 1::2::3::[]
1 + (sumlist 2::3::[])
1 + (2 + (sumlist 3::[]))
1 + (2 + (3 + (sumlist [])))
1 + (2 + (3 + 0))
Every function needs to return and then do more work.
*)
(* Some solutions
--------------
*)
(* Use an accumulating parameter to convert reverse from
quadradic to linear time. *)
let rev_a lst =
let rec revaccum lst accum =
match lst with
| [] -> accum
| x::xs -> revaccum xs (x::accum)
in
revaccum lst []
(* 1::2::3::4::[]
4::3::2::1::[] *)
(* Does the above function remind us of imperative programming? *)
(*- Here we are mimicing what we might do imperatively:
accum = []
while lst <> []
x::xs = lst
lst = xs
accum = x::accum
*)
(* We can also avoid stacking up the additions by using
an accumulating parameter. *)
let sumlist_a lst =
let rec accsum lst n =
match lst with
| [] -> n
| x::xs -> accsum xs (n+x)
in
accsum lst 0
(*- Here we want to just avoid stacking up the recursion.
Every call is the last thing the function needs to do.
Whatever the call retruns is the return value of the calling
function.
- if we delay addition a bit, evaluation is as follows:
sumlist_tr (1::2::3::[])
accum (1::2::3::[]) 0
accum (2::3::[]) (0+1)
accum (3::[]) ((0+1)+2)
accum [] (((0+1)+2)+3)
(((0+1)+2)+3)
Now we are adding from the front.
Does this remind us of imperative programming?
sumlist_tr [1;2;3] = (((0 + 1) + 2) + 3)
n = 0
while lst <> []
x::xs = lst
lst = xs
n = n+x
*)
(* Exercise: what is the tail recurive version of length? *)
(* A Tail recursive version of length *)
let length_tr lst =
let rec ltr lst len =
match lst with
| [] -> len
| _::xs -> ltr xs (len +1)
in
ltr lst 0
(* Fibonacci numbers *)
let rec fib n = match n with
| 0 -> 0
| 1 -> 1
| n -> fib (n-1) + fib (n-2)
(* What is the tail recursive version of the fib function
that uses accumulators to avoid all the recomputation?
0, 1, 1, 2, 3, 5, 8
*)
let fib_tr n =
let rec fib_acc a b n = match n with
| 0 -> a
| n -> fib_acc b (a+b) (n-1)
in
fib_acc 0 1 n
(* Another exercise: how does this relate to the imperative
version? *)
(* Let's recall sumlist and sumlist_tr.
Haven't we seen this before? *)
let rec foldl f v l =
match l with
| [] -> v
| x::xs -> foldl f (f v x) xs
let sum_f xs = foldl (+) 0 xs
(*-
foldl (+) 0 (1::2::3::[])
foldl (+) (0+1) (2::3::[])
foldl (+) 1 (2::3::[])
foldl (+) (1+2) (3::[])
foldl (+) 3 (3::[])
foldl (+) (3+3) []
foldl (+) 6 []
6
Or without evaluationg + so early
foldl (+) 0 (1::2::3::[])
foldl (+) (0+1) (2::3::[])
foldl (+) ((0+1)+2) (3::[])
foldl (+) (((0+1)+2)+3) []
(((0+1)+2)+3)
6
*)