The goal of this mini-project is to index strings, arrays, lists, lazy lists, streams, imperative lists, and binary trees.
The solutions for each question may vary from very short to semi-long. The questions about strings, arrays, lists, and streams are mandatory. The questions about lazy lists, imperative lists, and binary trees are marked as optional, but you need to solve one of them, and that is not optional.
Warning
Exceptions are a topic of study in Week 12 (Chapters Exceptions in OCaml and Programming using exceptions).
The goal of this section is to index a string with a function of type string -> int -> char option:
from left to right
Your implementation should satisfy the following unit tests:
let test_string_index_left_to_right candidate =
let b0 = (candidate "0123" 0 = Some '0')
and b1 = (candidate "0123" 1 = Some '1')
and b2 = (candidate "0123" 2 = Some '2')
and b3 = (candidate "0123" 3 = Some '3')
and b4 = (candidate "0123" 4 = None)
and b5 = (candidate "0123" ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
from right to left
Your implementation should satisfy the following unit tests:
let test_string_index_right_to_left candidate =
let b0 = (candidate "3210" 0 = Some '0')
and b1 = (candidate "3210" 1 = Some '1')
and b2 = (candidate "3210" 2 = Some '2')
and b3 = (candidate "3210" 3 = Some '3')
and b4 = (candidate "3210" 4 = None)
and b5 = (candidate "3210" ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
The goal of this section is to index an array with a function of type 'a array -> int -> 'a option:
from left to right
Your implementation should satisfy the following unit tests:
let test_array_index_left_to_right_int candidate =
let b0 = (candidate [|0; 1; 2; 3|] 0 = Some 0)
and b1 = (candidate [|0; 1; 2; 3|] 1 = Some 1)
and b2 = (candidate [|0; 1; 2; 3|] 2 = Some 2)
and b3 = (candidate [|0; 1; 2; 3|] 3 = Some 3)
and b4 = (candidate [|0; 1; 2; 3|] 4 = None)
and b5 = (candidate [|0; 1; 2; 3|] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_array_index_left_to_right_char candidate =
let b0 = (candidate [|'a'; 'b'; 'c'; 'd'|] 0 = Some 'a')
and b1 = (candidate [|'a'; 'b'; 'c'; 'd'|] 1 = Some 'b')
and b2 = (candidate [|'a'; 'b'; 'c'; 'd'|] 2 = Some 'c')
and b3 = (candidate [|'a'; 'b'; 'c'; 'd'|] 3 = Some 'd')
and b4 = (candidate [|'a'; 'b'; 'c'; 'd'|] 4 = None)
and b5 = (candidate [|'a'; 'b'; 'c'; 'd'|] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
from right to left
Your implementation should satisfy the following unit tests:
let test_array_index_right_to_left_int candidate =
let b0 = (candidate [|3; 2; 1; 0|] 0 = Some 0)
and b1 = (candidate [|3; 2; 1; 0|] 1 = Some 1)
and b2 = (candidate [|3; 2; 1; 0|] 2 = Some 2)
and b3 = (candidate [|3; 2; 1; 0|] 3 = Some 3)
and b4 = (candidate [|3; 2; 1; 0|] 4 = None)
and b5 = (candidate [|3; 2; 1; 0|] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_array_index_right_to_left_char candidate =
let b0 = (candidate [|'d'; 'c'; 'b'; 'a'|] 0 = Some 'a')
and b1 = (candidate [|'d'; 'c'; 'b'; 'a'|] 1 = Some 'b')
and b2 = (candidate [|'d'; 'c'; 'b'; 'a'|] 2 = Some 'c')
and b3 = (candidate [|'d'; 'c'; 'b'; 'a'|] 3 = Some 'd')
and b4 = (candidate [|'d'; 'c'; 'b'; 'a'|] 4 = None)
and b5 = (candidate [|'d'; 'c'; 'b'; 'a'|] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
The goal of this section is to index a list with a function of type 'a list -> int -> 'a option.
from left to right
Your implementation should satisfy the following unit tests:
let test_list_index_left_to_right_int candidate =
let b0 = (candidate [0; 1; 2; 3] 0 = Some 0)
and b1 = (candidate [0; 1; 2; 3] 1 = Some 1)
and b2 = (candidate [0; 1; 2; 3] 2 = Some 2)
and b3 = (candidate [0; 1; 2; 3] 3 = Some 3)
and b4 = (candidate [0; 1; 2; 3] 4 = None)
and b5 = (candidate [0; 1; 2; 3] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_list_index_left_to_right_char candidate =
let b0 = (candidate ['a'; 'b'; 'c'; 'd'] 0 = Some 'a')
and b1 = (candidate ['a'; 'b'; 'c'; 'd'] 1 = Some 'b')
and b2 = (candidate ['a'; 'b'; 'c'; 'd'] 2 = Some 'c')
and b3 = (candidate ['a'; 'b'; 'c'; 'd'] 3 = Some 'd')
and b4 = (candidate ['a'; 'b'; 'c'; 'd'] 4 = None)
and b5 = (candidate ['a'; 'b'; 'c'; 'd'] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
from right to left
Your implementation should satisfy the following unit tests:
let test_list_index_right_to_left_int candidate =
let b0 = (candidate [3; 2; 1; 0] 0 = Some 0)
and b1 = (candidate [3; 2; 1; 0] 1 = Some 1)
and b2 = (candidate [3; 2; 1; 0] 2 = Some 2)
and b3 = (candidate [3; 2; 1; 0] 3 = Some 3)
and b4 = (candidate [3; 2; 1; 0] 4 = None)
and b5 = (candidate [3; 2; 1; 0] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_list_index_right_to_left_char candidate =
let b0 = (candidate ['d'; 'c'; 'b'; 'a'] 0 = Some 'a')
and b1 = (candidate ['d'; 'c'; 'b'; 'a'] 1 = Some 'b')
and b2 = (candidate ['d'; 'c'; 'b'; 'a'] 2 = Some 'c')
and b3 = (candidate ['d'; 'c'; 'b'; 'a'] 3 = Some 'd')
and b4 = (candidate ['d'; 'c'; 'b'; 'a'] 4 = None)
and b5 = (candidate ['d'; 'c'; 'b'; 'a'] ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
Subsidiary questions:
The goal of this section is to index a lazy list with a function of type 'a llist -> int -> 'a option, where the polymorphic type llist is defined as follows:
type 'a llist =
| Lnil
| Lcons of 'a * 'a llist Lazy.t;;
from left to right
Your implementation should satisfy the following unit tests:
let test_llist_index_left_to_right_int candidate =
let b0 = (candidate (Lcons (0, lazy (Lcons (1, lazy (Lcons (2, lazy (Lcons (3, lazy Lnil)))))))) 0 = Some 0)
and b1 = (candidate (Lcons (0, lazy (Lcons (1, lazy (Lcons (2, lazy (Lcons (3, lazy Lnil)))))))) 1 = Some 1)
and b2 = (candidate (Lcons (0, lazy (Lcons (1, lazy (Lcons (2, lazy (Lcons (3, lazy Lnil)))))))) 2 = Some 2)
and b3 = (candidate (Lcons (0, lazy (Lcons (1, lazy (Lcons (2, lazy (Lcons (3, lazy Lnil)))))))) 3 = Some 3)
and b4 = (candidate (Lcons (0, lazy (Lcons (1, lazy (Lcons (2, lazy (Lcons (3, lazy Lnil)))))))) 4 = None)
and b5 = (candidate (Lcons (0, lazy (Lcons (1, lazy (Lcons (2, lazy (Lcons (3, lazy Lnil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_llist_index_left_to_right_char candidate =
let b0 = (candidate (Lcons ('a', lazy (Lcons ('b', lazy (Lcons ('c', lazy (Lcons ('d', lazy Lnil)))))))) 0 = Some 'a')
and b1 = (candidate (Lcons ('a', lazy (Lcons ('b', lazy (Lcons ('c', lazy (Lcons ('d', lazy Lnil)))))))) 1 = Some 'b')
and b2 = (candidate (Lcons ('a', lazy (Lcons ('b', lazy (Lcons ('c', lazy (Lcons ('d', lazy Lnil)))))))) 2 = Some 'c')
and b3 = (candidate (Lcons ('a', lazy (Lcons ('b', lazy (Lcons ('c', lazy (Lcons ('d', lazy Lnil)))))))) 3 = Some 'd')
and b4 = (candidate (Lcons ('a', lazy (Lcons ('b', lazy (Lcons ('c', lazy (Lcons ('d', lazy Lnil)))))))) 4 = None)
and b5 = (candidate (Lcons ('a', lazy (Lcons ('b', lazy (Lcons ('c', lazy (Lcons ('d', lazy Lnil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
from right to left
Your implementation should satisfy the following unit tests:
let test_llist_index_right_to_left_int candidate =
let b0 = (candidate (Lcons (3, lazy (Lcons (2, lazy (Lcons (1, lazy (Lcons (0, lazy Lnil)))))))) 0 = Some 0)
and b1 = (candidate (Lcons (3, lazy (Lcons (2, lazy (Lcons (1, lazy (Lcons (0, lazy Lnil)))))))) 1 = Some 1)
and b2 = (candidate (Lcons (3, lazy (Lcons (2, lazy (Lcons (1, lazy (Lcons (0, lazy Lnil)))))))) 2 = Some 2)
and b3 = (candidate (Lcons (3, lazy (Lcons (2, lazy (Lcons (1, lazy (Lcons (0, lazy Lnil)))))))) 3 = Some 3)
and b4 = (candidate (Lcons (3, lazy (Lcons (2, lazy (Lcons (1, lazy (Lcons (0, lazy Lnil)))))))) 4 = None)
and b5 = (candidate (Lcons (3, lazy (Lcons (2, lazy (Lcons (1, lazy (Lcons (0, lazy Lnil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_llist_index_right_to_left_char candidate =
let b0 = (candidate (Lcons ('d', lazy (Lcons ('c', lazy (Lcons ('b', lazy (Lcons ('a', lazy Lnil)))))))) 0 = Some 'a')
and b1 = (candidate (Lcons ('d', lazy (Lcons ('c', lazy (Lcons ('b', lazy (Lcons ('a', lazy Lnil)))))))) 1 = Some 'b')
and b2 = (candidate (Lcons ('d', lazy (Lcons ('c', lazy (Lcons ('b', lazy (Lcons ('a', lazy Lnil)))))))) 2 = Some 'c')
and b3 = (candidate (Lcons ('d', lazy (Lcons ('c', lazy (Lcons ('b', lazy (Lcons ('a', lazy Lnil)))))))) 3 = Some 'd')
and b4 = (candidate (Lcons ('d', lazy (Lcons ('c', lazy (Lcons ('b', lazy (Lcons ('a', lazy Lnil)))))))) 4 = None)
and b5 = (candidate (Lcons ('d', lazy (Lcons ('c', lazy (Lcons ('b', lazy (Lcons ('a', lazy Lnil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
The goal of this section is to index a stream with a function of type 'a stream -> int -> 'a option, where the polymorphic type stream is defined as follows:
type 'a stream =
| Scons of 'a * 'a stream Lazy.t;;
Subsidiary question: Is the option type needed here? Why?
The goal of this section is to index a list with a function of type 'a ilist -> int -> 'a option.
from left to right
Your implementation should satisfy the following unit tests:
let test_ilist_index_left_to_right_int candidate =
let b0 = (candidate (Icons (0, ref (Icons (1, ref (Icons (2, ref (Icons (3, ref Inil)))))))) 0 = Some 0)
and b1 = (candidate (Icons (0, ref (Icons (1, ref (Icons (2, ref (Icons (3, ref Inil)))))))) 1 = Some 1)
and b2 = (candidate (Icons (0, ref (Icons (1, ref (Icons (2, ref (Icons (3, ref Inil)))))))) 2 = Some 2)
and b3 = (candidate (Icons (0, ref (Icons (1, ref (Icons (2, ref (Icons (3, ref Inil)))))))) 3 = Some 3)
and b4 = (candidate (Icons (0, ref (Icons (1, ref (Icons (2, ref (Icons (3, ref Inil)))))))) 4 = None)
and b5 = (candidate (Icons (0, ref (Icons (1, ref (Icons (2, ref (Icons (3, ref Inil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_ilist_index_left_to_right_char candidate =
let b0 = (candidate (Icons ('a', ref (Icons ('b', ref (Icons ('c', ref (Icons ('d', ref Inil)))))))) 0 = Some 'a')
and b1 = (candidate (Icons ('a', ref (Icons ('b', ref (Icons ('c', ref (Icons ('d', ref Inil)))))))) 1 = Some 'b')
and b2 = (candidate (Icons ('a', ref (Icons ('b', ref (Icons ('c', ref (Icons ('d', ref Inil)))))))) 2 = Some 'c')
and b3 = (candidate (Icons ('a', ref (Icons ('b', ref (Icons ('c', ref (Icons ('d', ref Inil)))))))) 3 = Some 'd')
and b4 = (candidate (Icons ('a', ref (Icons ('b', ref (Icons ('c', ref (Icons ('d', ref Inil)))))))) 4 = None)
and b5 = (candidate (Icons ('a', ref (Icons ('b', ref (Icons ('c', ref (Icons ('d', ref Inil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
So:
from right to left
Your implementation should satisfy the following unit tests:
let test_ilist_index_right_to_left_int candidate =
let b0 = (candidate (Icons (3, ref (Icons (2, ref (Icons (1, ref (Icons (0, ref Inil)))))))) 0 = Some 0)
and b1 = (candidate (Icons (3, ref (Icons (2, ref (Icons (1, ref (Icons (0, ref Inil)))))))) 1 = Some 1)
and b2 = (candidate (Icons (3, ref (Icons (2, ref (Icons (1, ref (Icons (0, ref Inil)))))))) 2 = Some 2)
and b3 = (candidate (Icons (3, ref (Icons (2, ref (Icons (1, ref (Icons (0, ref Inil)))))))) 3 = Some 3)
and b4 = (candidate (Icons (3, ref (Icons (2, ref (Icons (1, ref (Icons (0, ref Inil)))))))) 4 = None)
and b5 = (candidate (Icons (3, ref (Icons (2, ref (Icons (1, ref (Icons (0, ref Inil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
let test_ilist_index_right_to_left_char candidate =
let b0 = (candidate (Icons ('d', ref (Icons ('c', ref (Icons ('b', ref (Icons ('a', ref Inil)))))))) 0 = Some 'a')
and b1 = (candidate (Icons ('d', ref (Icons ('c', ref (Icons ('b', ref (Icons ('a', ref Inil)))))))) 1 = Some 'b')
and b2 = (candidate (Icons ('d', ref (Icons ('c', ref (Icons ('b', ref (Icons ('a', ref Inil)))))))) 2 = Some 'c')
and b3 = (candidate (Icons ('d', ref (Icons ('c', ref (Icons ('b', ref (Icons ('a', ref Inil)))))))) 3 = Some 'd')
and b4 = (candidate (Icons ('d', ref (Icons ('c', ref (Icons ('b', ref (Icons ('a', ref Inil)))))))) 4 = None)
and b5 = (candidate (Icons ('d', ref (Icons ('c', ref (Icons ('b', ref (Icons ('a', ref Inil)))))))) ~-1 = None)
in b0 && b1 && b2 && b3 && b4 && b5;;
Subsidiary question:
This section assumes that Exercise 7, in Week 13, was solved. Its goal is to index a binary tree with a function of type 'a binary_tree -> int -> 'a option, where the polymorphic type binary_tree is defined as follows:
type 'a binary_tree =
| Leaf of 'a
| Node of 'a binary_tree * 'a binary_tree;;
The tree traversals should be:
Fixed the statement of Question 2.b in the section about indexing arrays, thanks to Marcellinus Jerricho’s keen eye [16 Apr 2021]
Fixed a typo in a test for lazy lists, thanks to Akanksha Chokshi and Le Nguyen Phong’s eagle eye [15 Apr 2021]
Added a collection of unit-test functions using the char type, not only the int type [14 Apr 2021]
Fixed the reference to Exercise 7, thanks to Aditya Singhania’s eagle eye [12 Apr 2021]
Adjusted the type of the indexing function in the question about Indexing streams as well as the optional questions [11 Apr 2021]
Created [03 Apr 2021]