3.14. Exercises#
Solutions to most exercises are available. Fall 2022 is the first public release of these solutions. Though they have been available to Cornell students for a few years, it is inevitable that wider circulation will reveal improvements that could be made. We are happy to add or correct solutions. Please make contributions through GitHub.
Exercise: list expressions [★]
Construct a list that has the integers 1 through 5 in it. Use the square bracket notation for lists.
Construct the same list, but do not use the square bracket notation. Instead, use
::
and[]
.Construct the same list again. This time, the following expression must appear in your answer:
[2; 3; 4]
. Use the@
operator, and do not use::
.
Exercise: product [★★]
Write a function product
that returns the product of all the elements in a
list. The product of all the elements of an empty list is 1
.
Exercise: concat [★★]
Write a function that concatenates all the strings in a list. The concatenation
of all the strings in an empty list is the empty string ""
.
Exercise: product test [★★]
Unit test the function product
that you wrote in an exercise above.
Exercise: patterns [★★★]
Using pattern matching, write three functions, one for each of the following
properties. Your functions should return true
if the input list has the
property and false
otherwise.
the list’s first element is
"bigred"
the list has exactly two or four elements; do not use the
length
functionthe first two elements of the list are equal
Exercise: library [★★★]
Consult the List
standard library to solve these exercises:
Write a function that takes an
int list
and returns the fifth element of that list, if such an element exists. If the list has fewer than five elements, return0
. Hint:List.length
andList.nth
.Write a function that takes an
int list
and returns the list sorted in descending order. Hint:List.sort
withStdlib.compare
as its first argument, andList.rev
.
Exercise: library test [★★★]
Write a couple OUnit unit tests for each of the functions you wrote in the previous exercise.
Exercise: library puzzle [★★★]
Write a function that returns the last element of a list. Your function may assume that the list is non-empty. Hint: Use two library functions, and do not write any pattern matching code of your own.
Write a function
any_zeros : int list -> bool
that returnstrue
if and only if the input list contains at least one0
. Hint: use one library function, and do not write any pattern matching code of your own.
Your solutions will be only one or two lines of code each.
Exercise: take drop [★★★]
Write a function
take : int -> 'a list -> 'a list
such thattake n lst
returns the firstn
elements oflst
. Iflst
has fewer thann
elements, return all of them.Write a function
drop : int -> 'a list -> 'a list
such thatdrop n lst
returns all but the firstn
elements oflst
. Iflst
has fewer thann
elements, return the empty list.
Exercise: take drop tail [★★★★]
Revise your solutions for take
and drop
to be tail recursive, if they aren’t
already. Test them on long lists with large values of n
to see whether they
run out of stack space. To construct long lists, use the --
operator from
the lists section.
Exercise: unimodal [★★★]
Write a function is_unimodal : int list -> bool
that takes an integer list and
returns whether that list is unimodal. A unimodal list is a list that
monotonically increases to some maximum value then monotonically decreases after
that value. Either or both segments (increasing or decreasing) may be empty. A
constant list is unimodal, as is the empty list.
Exercise: powerset [★★★]
Write a function powerset : int list -> int list list
that takes a set S
represented as a list and returns the set of all subsets of S. The order of
subsets in the powerset and the order of elements in the subsets do not matter.
Hint: Consider the recursive structure of this problem. Suppose you already
have p
, such that p = powerset s
. How could you use p
to compute
powerset (x :: s)
?
Exercise: print int list rec [★★]
Write a function print_int_list : int list -> unit
that prints its input list,
one number per line. For example, print_int_list [1; 2; 3]
should result in
this output:
1
2
3
Here is some code to get you started:
let rec print_int_list = function
| [] -> ()
| h :: t -> (* fill in here *); print_int_list t
Exercise: print int list iter [★★]
Write a function print_int_list' : int list -> unit
whose specification is the
same as print_int_list
. Do not use the keyword rec
in your solution, but
instead to use the List module function List.iter
. Here is some code
to get you started:
let print_int_list' lst =
List.iter (fun x -> (* fill in here *)) lst
Exercise: student [★★]
Assume the following type definition:
type student = {first_name : string; last_name : string; gpa : float}
Give OCaml expressions that have the following types:
student
student -> string * string
(a function that extracts the student’s name)string -> string -> float -> student
(a function that creates a student record)
Exercise: pokerecord [★★]
Here is a variant that represents a few Pokémon types:
type poketype = Normal | Fire | Water
Define the type
pokemon
to be a record with fieldsname
(a string),hp
(an integer), andptype
(apoketype
).Create a record named
charizard
of typepokemon
that represents a Pokémon with 78 HP and Fire type.Create a record named
squirtle
of typepokemon
that represents a Pokémon with 44 HP and Water type.
Exercise: safe hd and tl [★★]
Write a function safe_hd : 'a list -> 'a option
that returns Some x
if the
head of the input list is x
, and None
if the input list is empty.
Also write a function safe_tl : 'a list -> 'a list option
that returns the
tail of the list, or None
if the list is empty.
Exercise: pokefun [★★★]
Write a function max_hp : pokemon list -> pokemon option
that, given a list of
pokemon
, finds the Pokémon with the highest HP.
Exercise: date before [★★]
Define a date-like triple to be a value of type int * int * int
. Examples of
date-like triples include (2013, 2, 1)
and (0, 0, 1000)
. A date is a
date-like triple whose first part is a positive year (i.e., a year in the common
era), second part is a month between 1 and 12, and third part is a day between 1
and 31 (or 30, 29, or 28, depending on the month and year). (2013, 2, 1)
is a
date; (0, 0, 1000)
is not.
Write a function is_before
that takes two dates as input and evaluates to
true
or false
. It evaluates to true
if the first argument is a date that
comes before the second argument. (If the two dates are the same, the result is
false
.)
Your function needs to work correctly only for dates, not for arbitrary date-like triples. However, you will probably find it easier to write your solution if you think about making it work for arbitrary date-like triples. For example, it’s easier to forget about whether the input is truly a date, and simply write a function that claims (for example) that January 100, 2013 comes before February 34, 2013—because any date in January comes before any date in February, but a function that says that January 100, 2013 comes after February 34, 2013 is also valid. You may ignore leap years.
Exercise: earliest date [★★★]
Write a function earliest : (int*int*int) list -> (int * int * int) option
. It
evaluates to None
if the input list is empty, and to Some d
if date d
is
the earliest date in the list. Hint: use is_before
.
As in the previous exercise, your function needs to work correctly only for dates, not for arbitrary date-like triples.
Exercise: assoc list [★]
Use the functions insert
and lookup
from the
section on association lists to construct an association list that
maps the integer 1 to the string “one”, 2 to “two”, and 3 to “three”. Lookup the
key 2. Lookup the key 4.
Exercise: cards [★★]
Define a variant type
suit
that represents the four suits, ♣ ♦ ♥ ♠, in a standard 52-card deck. All the constructors of your type should be constant.Define a type
rank
that represents the possible ranks of a card: 2, 3, …, 10, Jack, Queen, King, or Ace. There are many possible solutions; you are free to choose whatever works for you. One is to makerank
be a synonym ofint
, and to assume that Jack=11, Queen=12, King=13, and Ace=1 or 14. Another is to use variants.Define a type
card
that represents the suit and rank of a single card. Make it a record with two fields.Define a few values of type
card
: the Ace of Clubs, the Queen of Hearts, the Two of Diamonds, the Seven of Spades.
Exercise: matching [★]
For each pattern in the list below, give a value of type int option list
that
does not match the pattern and is not the empty list, or explain why that’s
impossible.
Some x :: tl
[Some 3110; None]
[Some x; _]
h1 :: h2 :: tl
h :: tl
Exercise: quadrant [★★]
Complete the quadrant
function below, which should return the quadrant of the
given x, y
point according to the diagram on the right (borrowed from
Wikipedia).
Points that lie on an axis do not belong to any quadrant. Hints: (a) define a
helper function for the sign of an integer, (b) match against a pair.
type quad = I | II | III | IV
type sign = Neg | Zero | Pos
let sign (x:int) : sign =
...
let quadrant : int*int -> quad option = fun (x,y) ->
match ... with
| ... -> Some I
| ... -> Some II
| ... -> Some III
| ... -> Some IV
| ... -> None
Exercise: quadrant when [★★]
Rewrite the quadrant function to use the when
syntax. You won’t need your
helper function from before.
let quadrant_when : int*int -> quad option = function
| ... when ... -> Some I
| ... when ... -> Some II
| ... when ... -> Some III
| ... when ... -> Some IV
| ... -> None
Exercise: depth [★★]
Write a function depth : 'a tree -> int
that returns the number of nodes in
any longest path from the root to a leaf. For example, the depth of an empty
tree (simply Leaf
) is 0
, and the depth of tree t
above is 3
. Hint:
there is a library function max : 'a -> 'a -> 'a
that returns the maximum of
any two values of the same type.
Exercise: shape [★★★]
Write a function same_shape : 'a tree -> 'b tree -> bool
that determines
whether two trees have the same shape, regardless of whether the values they
carry at each node are the same. Hint: use a pattern match with three branches,
where the expression being matched is a pair of trees.
Exercise: list max exn [★★]
Write a function list_max : int list -> int
that returns the maximum integer
in a list, or raises Failure "list_max"
if the list is empty.
Exercise: list max exn string [★★]
Write a function list_max_string : int list -> string
that returns a string
containing the maximum integer in a list, or the string "empty"
(note, not the
exception Failure "empty"
but just the string "empty"
) if the list is empty.
Hint: string_of_int
in the standard library will do what its name suggests.
Exercise: list max exn ounit [★]
Write two OUnit tests to determine whether your solution to list max exn, above, correctly raises an exception when its input is the empty list, and whether it correctly returns the max value of the input list when that list is non-empty.
Exercise: is_bst [★★★★]
Write a function is_bst : ('a*'b) tree -> bool
that returns true
if and only
if the given tree satisfies the binary search tree invariant. An efficient
version of this function that visits each node at most once is somewhat tricky
to write. Hint: write a recursive helper function that takes a tree and either
gives you (i) the minimum and maximum value in the tree, or (ii) tells you that
the tree is empty, or (iii) tells you that the tree does not satisfy the
invariant. Your is_bst
function will not be recursive, but will call your
helper function and pattern match on the result. You will need to define a new
variant type for the return type of your helper function.
Exercise: quadrant poly [★★]
Modify your definition of quadrant to use polymorphic variants. The types of your functions should become these:
val sign : int -> [> `Neg | `Pos | `Zero ]
val quadrant : int * int -> [> `I | `II | `III | `IV ] option