On this page:
Updates
Introduction
Advice
Part 0:   Binary trees
Part 1:   Boolean Expressions and Vectors
Part 2:   Lambda Calculus
Part 3:   Unit Testing
What to Turn In
Academic Integrity
6.8

Project 1: OCaml Warmup

Due: Thur, Feb 16, 2017 11:59:59pm

Updates
Introduction

This project will help refresh your memory on programming in OCaml by asking you to write two small modules and then develop test cases for them. You may find it helpful to review the OCaml slides from CMSC 330.

Advice
Part 0: Binary trees

This is not part of the project; it is only here as a warm-up to the rest. Try to do these finger exercises first. If you have trouble, don’t try to proceed to the rest of the project, but rather try to master these simpler concepts first. You do not have submit this code and it will not be graded if you do.

Here’s a type for binary trees:

type 'a bt =

  | Leaf

  | Node of 'a * 'a bt * 'a bt

Implement the following functions:

(** does the tree contain an element equal to the given one? *)

val contains : 'a bt -> 'a -> bool

 

(** produce elements in the left-to-right order they appear in the tree *)

val to_list : 'a bt -> 'a list

 

(** zip together two binary trees with same shape; fail if not same shape *)

val zip : 'a bt -> 'b bt -> ('a * 'b) bt

 

(** produce tree of same shape, every element is obtained by applying f *)

val map : 'a bt -> ('a -> 'b) -> 'b bt

 

(** produce the mirror image of a tree; elements are in reverse order *)

val rev : 'a bt -> 'a bt

 

(** list List.fold but for binary trees *)

val fold : 'a bt -> 'b -> ('a -> 'b -> 'b -> 'b) -> 'b

A path is a list of directions, which are either left or right, and can describe a path to an element in a binary tree:

type dir = Left | Right

Write a function the gets the element found by following a given path. This function should fail if the path goes off the end of the tree:

(** get the element found by following given path *)

val get : 'a bt -> dir list -> 'a

A binary search tree is binary tree with an ordering on elements such that all elements in the left subtree of a node are less than or equal the value of the node and all elements in the right subtree.

Here’s one possible type for binary search trees:

type 'a bst = ('a -> 'a -> bool) * 'a bt

Think of the comparison function like (<=).

Implement the following functions:

(** insert the given element into bst *)

val bst_insert : 'a bst -> 'a -> 'a bst

 

(** does the bst contain an element equal to the given one? *)

val bst_contains : 'a bst -> 'a -> bool

 

(** delete the first occurrence of given element; fail if it doesn't exist *)

val bst_delete : 'a bst -> 'a -> 'a bst

Using your implementation of binary search trees, implement a sorting algorithm that consumes a list of elements and produce the same list of elements but in sorted order.

val sort : ('a -> 'a -> bool) -> 'a list -> 'a list

Part 1: Boolean Expressions and Vectors

In this part of the project, you will develop some functions for working with boolean expressions and vectors of boolean expressions. We will use the following data type to represent boolean expressions:

type expr =

    EFalse

  | ETrue

  | EVar of string

  | EAnd of expr * expr

  | EOr of expr * expr

  | ENot of expr

  | EForall of string * expr

  | EExists of string * expr

Here EFalse and ETrue represent the obvious values. EVar s represents the boolean variable with name s. The constructors EAnd, EOr, and ENot represent boolean conjunction, disjunction, and negation, respectively. For example, the expression (a or b) and c could be represented as EAnd (EOr (EVar "a", EVar "b"), EVar "c"). (We’ll explain EForall and EExists in a moment.) We will use associative lists, which are just lists of pairs, to assign truth values to variables:

type asst = (string * bool) list

Here if an asst contains the pair (s, b), then that assignment gives the variable represented by the string s the value b. When working with the type asst, you will find the functions List.assoc, List.mem_assoc, and related functions helpful. See the OCaml library documentation for more details. You may assume for purposes of this project that whenever you work with an asst, variables on the left override variables on the right. E.g., the assignment ["a", true; "a", false] assigns true to Var "a".

The last two expressions, EForall and EExists, represent the similarly named quantifiers. The expression EForall(x,e) is true if e is true when x is true and when x is false. Similarly, EExists(x,e) is true if e is true either when x is true or when x is false.

However complex a boolean expression, it represents a single bit. We can put those bits together to represent integers using the following type:

type bvec = expr list (* low order bit at head of list *)

A bvec is a list of boolean expressions representing bits, with the low order bit at the head of the list. For example, we could represent the number 13 as [ETrue; EFalse; ETrue; ETrue]. Zero could be represented as [] or [EFalse] or [EFalse; EFalse], etc. An unknown 4-bit integer could be represented as [EVar "a"; EVar "b"; EVar "c"; EVar "d"]. You can find the definitions of the above types in boolean.mli, along with type signatures for several functions you must implement in boolean.ml:

If you’ve gotten this far, you’ve now built a (likely very slow) solver for part of bitvector arithmetic! Suppose we have:

let x = [EVar "a"; EVar "b"]

let y = [EVar "c"; EVar "d"]

let z = [EFalse; EFalse; ETrue]  (* 4 *)

Then we can use our solver to answer questions like:

sat (eq (add x y) z)  (* are there two, two-bit numbers that sum to 4? *)

sat (eq (add x x) z)  (* is there a two-bit number that, doubled, is 4? *)

etc. Cool!

Part 2: Lambda Calculus

Your next task is to implement a few functions related to Lambda calculus, which you learned about in CMSC 330. Below is an interface file "lambda.mli". You must implement the last seven of the listed functions in "lambda.ml". (We’ve already implemented unparse for you.)

type expr =

| Var of string

| Lam of string * expr

| App of expr * expr

 

val unparse : expr -> string

 

val free_vars : expr -> string list

val is_alpha : expr -> expr -> bool

val subst : string -> expr -> expr -> expr

val beta : expr -> expr option

val normal_form : expr -> expr

val expr_of_int : int -> expr

val add : expr -> expr -> expr

These functions should do the following:

Part 3: Unit Testing

Your last task is to write unit tests for parts 1 and 2 above, using OUnit. Put your test cases in the file "test.ml", and be sure to add your test cases to the suites suite_trie and suite_lambda, as appropriate. We will grade this part of the project by running your test cases against various correct and incorrect implementations. A test case will only be counted as correct if it passes a correct implementation and fails at least one incorrect implementation.

What to Turn In

Put all your code in the code skeleton we have supplied, and upload your solution to the submit server. You can either upload your solution via the web interface, or you can run java -jar submit.jar in the project directory.

Important note: On GRACE you should use the version of OCaml installed in the public directory for this course. It is version 4.04.0 and has the ocamlfind and ounit libraries pre-installed. To access this version of ocaml, run the following command after logging in to grace.umd.edu:

$ source /afs/glue.umd.edu/class/spring2017/cmsc/430/0101/public/.opam/opam-init/init.csh

You can confirm you have the new version of OCaml as follows:

$ ocaml -version

The OCaml toplevel, version 4.04.0

If you want to make so that you always use the new version of OCaml, you can create (or edit, if it exists) the file .startup.tty in your home directory and add the source command from above. Running the following will add this line to that file. After doing this, the newer version of OCaml will be available when you log in without having to the source thing every time:

$ touch .startup.tty

$ echo 'source /afs/glue.umd.edu/class/spring2017/cmsc/430/0101/public/.opam/opam-init/init.csh' >> .startup.tty

To test this, log out, log back in. If you run ocaml, you should see 4.04.0.

You should now be able to download, unpack, and compile the code skeleton for Project 1 on GRACE. Here’s a sample, showing how to do it:

$ curl -s http://www.cs.umd.edu/class/spring2017/cmsc430/p1.tar.gz | tar -xz

$ cd p1/

$ make

ocamlbuild main.native -pkgs oUnit

Finished, 7 targets (0 cached) in 00:00:00.

$ ./main.native

<....testing output....>

Ran: 1 tests in: 0.00 seconds.

OK

Please post any questions you have about this on Piazza. You are welcome to install OCaml on your own machine, though we can’t provide much support for that. We do recommend using opam to manage the installation process if you choose to do that. You can use opam to install both OCaml and OUnit.

Academic Integrity

The Campus Senate has adopted a policy asking students to include the following statement on each assignment in every course: “I pledge on my honor that I have not given or received any unauthorized assistance on this assignment.” Consequently your program is requested to contain this pledge in a comment near the top.

Please carefully read the Academic Integrity section of the course syllabus. Any evidence of impermissible cooperation on projects, use of disallowed materials or resources, or unauthorized use of computer accounts, will be submitted to the Student Honor Council, which could result in an XF for the course, or suspension or expulsion from the University. Be sure you understand what you are and what you are not permitted to do in regards to academic integrity when it comes to project assignments. These policies apply to all students, and the Student Honor Council does not consider lack of knowledge of the policies to be a defense for violating them. Full information is found in the course syllabus—please review it at this time.

 

Web Accessibility