Write a function evens
which does the opposite to odds
, returning the even
numbered elements in a list. For example, evens [2; 4; 2; 4; 2]
should return
[4; 4]
. What is the type of your function?
(* 'a list -> 'a list *)
let rec evens xs =
match xs with
| _::y::ys -> y :: evens ys
| _ -> []
;;
Write a function count_true
which counts the number of true
elements in a
list. For example, count_true [true; false; true]
should return 2
. What is
the type of your function? Can you write a tail recursive version?
(* bool list -> int *)
let count_true xs =
let rec go xs c =
match xs with
| [] -> c
| true::tl -> go tl (c + 1)
| false::tl -> go tl c
in
go xs 0
;;
Write a function which, given a list, builds a palindrome from it. A
palindrome is a list which equals its own reverse. You can assume the existence
of rev
and @
. Write another function which determines if a list is a
palindrome.
let make_palindrome xs =
xs @ (rev xs)
;;
let is_palindrome xs =
xs = (rev xs)
;;
Write a function drop_last
which returns all but the last element of a list.
If the list is empty, it should return the empty list. So, for example,
drop_last [1; 2; 4; 8]
should return [1; 2; 4]
. What about a tail recursive
version?
let rec drop_last xs =
match xs with
| [] | [_] -> []
| y::ys -> y :: drop_last ys
;;
let drop_last_opt xs =
let rec go xs acc =
match xs with
| [] | [_] -> rev acc
| y::ys -> go ys (y :: acc)
in
go xs []
;;
Write a function member of type α → α list → bool
which returns true
if an
element exists in a list, or false
if not. For example, member 2 [1; 2; 3]
should evaluate to true
, but member 3 [1; 2]
should evaluate to false
.
let rec member t xs =
match xs with
| [] -> false
| y::ys -> y = t || member t ys
;;
Use your member function to write a function make_set
which, given a list,
returns a list which contains all the elements of the original list, but has no
duplicate elements. For example, make_set [1; 2; 3; 3; 1]
might return
[2; 3; 1]
. What is the type of your function?
let make_set xs =
let rec go xs set =
match xs with
| [] -> set
| y::ys -> if member y set then go ys set else go ys (y :: set)
in
go xs []
;;
Can you explain why the rev
function we defined is inefficient? How does the
time it takes to run relate to the size of its argument? Can you write a more
efficient version using an accumulating argument? What is its efficiency in
terms of time taken and space used?
(* Provided `rev`, for reference. *)
let rec rev l = match l with
| [] -> []
| h::t -> rev t @ [h]
;;
We know that the
@
operator takesO(n)
time to run, wheren
is the length of the list on the left-hand side (this is told to us on page 26, and proved on page 29). So we will be running anO(n)
operationn
times. So, I think, the time complexity isO(n^2)
. As for space complexity it would beO(n^2)
as well, I think.
(* I think both time and space should be O(n) *)
let rev_opt xs =
let rec go xs acc =
match xs with
| [] -> []
| y::ys -> go ys (y :: acc)
in
go xs []
;;