Ken Wakita (https://wakita.github.io/fp2018/)
Oct. 4, 2018
A record can be regarded as a tuple whose elements are accessible by names. For example a ratio
record as defined below can be regarded as a pair of int
’s whose elements are referenced by num
and denom
.
A record data constructed by the following syntax:
And names are used to access its elements:
Variant types can be used to represent a member of finite flags/sets/states/tags. At first, it may look like enum
type in C-like languages. For example, the sign
type denotes the domain that is either positive or negative.
Positive
and Negative
are called constructors of the sign
type.
As we use switch
syntax to identify a enum
value, we use pattern matching in OCaml:
let sign_int n =
if n >= 0 then Positive
else Negative;;
Like Java’s Enum
type, members of a variant type can have values. In this case, the member’s declaration contains its type: For example, the color
type represents colors by several popular colors (black, white, red, …, cyan), and others that are expressed by combinations of red-, green-, blue-intensity.
type color = Black | White | Red | ... | Cyan | RGB of int * int * int;;
let colors = [Black; White; RGB(255, 0, 0); RGB(0, 255, 0); RGB(0, 0, 255)];;
We use pattern matching to identify the kind of color
type and its value if any.
A recursive data type can be easily defined by referencing the type name being defined in its definition. Let’s look at the definition of the 'a binary_tree
type:
type 'a binary_tree =
Empty
| Node of 'a * 'a binary_tree * 'a binary_tree;;
type 'a binary_tree = Empty | Node of 'a * 'a binary_tree * 'a binary_tree
It offers two constructors: Empty
represents a leaf of a tree and Node
an internal node. The Node
constructor contains a triple of a value of type 'a
and two subtrees both of type 'a binary_tree
.
Node(4, Node(2, Node(1, Empty, Empty), Node(3, Empty, Empty)), Empty)
- : int binary_tree =
Node (4, Node (2, Node (1, Empty, Empty), Node (3, Empty, Empty)), Empty)
Empty
and Node
, the label of the circle its value, and arrows references to subtrees.A function that manipulates of trees can simply be defined by use of pattern matching.
Constructing a recursive data while traversing another. The definition below gives a new binary tree that
values
Define a function named values
that takes a binary tree and gives a list of values contained in the tree following the order that occur from left to right in the tree.
values Node(4, Node(2, Node(1, Empty, Empty), Node(3, Empty, Empty)), Node(5, Empty, Empty))
should give [1; 2; 3; 4; 5]
.
values
(* This implimentation is not efficient because of
its use of the append operator (`@`).
The comptational complexity is a square of the size of the tree.
Can you guess its worst case scenario? *)
let rec values t =
match t with
| Empty -> []
| Node(x, left, right) -> values left @ [x] @ values right;;
values
mirror
Define a function named mirror
that takes a binary tree and construct a mirror image of the given tree.
mirror
of the treemirror
is_binary_search_tree
It is important for the binary search tree that its elements are sorted. If it is not the case the member
and insert
do not work as we expect: e.g. member 1 (Node(2, Empty, (Node(1, Empty, Empty))))
fails to find 1
contained in the tree.
Define a function name is_binary_search_tree
that takes a tree and tells if it is a binary search tree.
is_binary_search_tree
let is_binary_search_tree t =
let rec aux test t =
match t with
| Empty -> true
| Node(y, left, right) ->
test y &&
aux (function x -> test x && x < y) left &&
aux (function x -> test x && x > y) right in
aux (function _ -> true) t;;
Unfortunately the computational complexity of this solution is quadratic of the tree size. Two more efficient solutions are found in the complete answers slide.
Complete answers to the exercises:
Download 03-exercise.ml
Download 03-exercise.ml
The second coding assignment will be posted tonight at OCW-i. The due date will be presented as well.