csci2041/public-class-repo/Labs/Lab_03_Jan_31.md
Michael Zhang 399845160c
f
2018-01-29 17:35:31 -06:00

7.9 KiB

Lab 3: Improving your OCaml code

CSci 2041: Advanced Programming Principles, Spring 2017

Due: Friday, February 3 at 5:00pm. You should be able to complete the discussion portion of this lab work during the lab. But you may need additional time to complete the work on your functions to make sure that they meet the new requirements for writing "good" code. This work needs to be completed by the due date.

Introduction

Goals of this lab:

In this lab you will work with 1 or 2 of your classmates to improve the structure and style of your OCaml functions. You will view your classmates solutions to Hwk 01 and allow them to view your solution.

This sort of sharing is only allowed, obviously, after the due date.

You will also

  1. remove all ;; since they are not needed in an OCaml file; they only indicate the end of the input when typing a declaration or expression into the interpreter,

  2. remove a clumsy list construction idiom if you are using it (see below), and

  3. improve your functions so that there are no warning messages generated when loading your file into OCaml. (Again, see below).

Details about how to make these changes are provided below.

Form groups.

Find 1 or 2 people that you want to work with in lab. Feel free to move around in lab to sit next to the people you are going to work with. Introduce yourselves.

You will all want to log into the computer on your desk. Then you should log into GitHub and open the page with hwk_01.ml so that your solution is visible.

Prepare the files for lab 3

Create a new directory named Lab_03 and copy your hwk_01.ml file from Hwk_01 into this new directory.

At the top of this new file, add a comment with your name. Also include the names of the other member or members of your group.

Determine questions to share.

As a group, you need to decide which 3 functions you want to share with others in your group, discuss, and then improve your implementation of that function.

Do not choose the very simple functions even, frac_add, or frac_mul.

Discuss the first function.

In this part, you will move this selected function to the top of your new hwk_01.ml file (after the comment with the names) and add a new comment above that describes the changes that you made to the function to address the concerns described below. You must also specify if you are borrowing code from another student and give them proper credit for their work.

Read this entire section before making any more changes to your new hwk_01.ml file.

Each of you needs to explain your solution of the first function to the other 1 or 2 members of your group. Take turns doing this.

After (or during) this, discuss the characteristics of the different implementations that you think represent well-written functions. Also discuss the characteristics of your functions that you think that you can improve.

In doing so, consider the 3 points of improvement listed above. Fixing the first 1 is easy. Read the next two sections to understand how to address the more interesting second and third concerns. Continue your discussion after you've read over these two sections.

clumsy list construction

When constructing a new list, only use the append operator (@) when the left argument to @ is a list for which you do not know the size.

Do not write, for example, [ x ] @ my_function rest.

Instead use the cons operator (::) and write x :: my_function rest.

fixing all warnings

The automated tests for this lab will reject your code if there are any warning generated for it. Thus you'll need to fix those.

For example, you may have written a head function to return the first element of a list as follows:

let head = function
  | x::xs -> x

This would yield a warning message like the following:

File "hwk_01.ml", line 42, characters 4-144:
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]

telling you that your function doesn't have a clause for the empty list ([]).

The head function with type 'a list -> `a can only work if it is given a non-empty list. If it is called with an empty list, this is an error in the call of the function, not the function itself. Thus, the author of head is justified in raising an exception if this input is the empty list. Thus the function should be implemented as follows:

let head = function
  | x::xs -> x
  | _ -> raise (Failure "Incorrect input: empty list passed to head")

The idea here is that functions need to be explicit about what kind of input they can accept. The type of the function is a good first approximation of this. The type indicates what "type" of data can be passed in, and what "type" of data will be returned. The type system will ensure that these requirement will be satisfied by any call to the function. Any function call with the incorrect types will be rejected by the type checker.

But this isn't quite enough for functions like head, tail, max_list, or matrix_transpose. These function have additional requirements on their inputs that cannot be specified by the type system. For example, that a list is not empty.

If these cases, it is appropriate to raise an exception with a message stating that the input provided to the function is not correct, or does not meet the specified requirements of the function.

Note that a function like reverse (rev) must work for all possible inputs (that don't have type errors) and thus, for this assignment, there must be no raise constructs in those functions.

Part of your revision of each function is to ensure that none of your functions have any warning when loaded into OCaml. For some, this will require raising an exception in the appropriate place.

Below is a list of when it is allowed to raise exceptions. Please consult this in your work.

Which functions can raise exceptions?

  • drop, rev, and perimeter must work on all (type-correct) input and thus must not have any raise constructs in their implementation.

  • euclid can raise an exception if either of its inputs is not a positive integer.

  • frac_add can raise an exception if the denominator is 0. But it is OK if you do not and simply do the computation.

  • frac_simplify can raise an exception if the denominator is 0.

  • square_approx can raise an exception if the input is not greater than 1.0.

  • max_list can raise an exception if the list is empty. This is better than returning 0 for the empty list.

  • matrix_scalar_add can raise an exception if the input is not a matrix. Your function is_matrix is now useful here.

  • The bonus-round problems matrix_transpose can raise an exception if its input is not

  • The bonus-round problem matrix_multiply can raise an exception if either input is not a matrix or if their sizes are such that they cannot be multiplied.

  • Helper functions such as head or tail can raise appropriate exceptions. But most other helper functions probably don't need to.

Repeat this process for the next 2 functions that you have chosen.

If you run low on time and can only complete 2 functions that is OK.

What to turn in.

You need to turn in your new hwk_01.ml file in the new Lab_03 directory via GitHub.

This file must contain your name and the names of those you worked with.

For each function that your worked on with your group, you must provide a detailed comment above it explaining the changes that you made and explain how they improve the implementation of your function. You must also indicate where the ideas for the changes came from - that is, who in your group contributed them.

For the remainder of the functions, use what you learned from these discussions with your classmates to make sure that none of them produce any warnings and raise exceptions with an appropriate message and only in the cases described above.