This commit is contained in:
Michael Zhang 2018-01-29 17:35:31 -06:00
commit 399845160c
No known key found for this signature in database
GPG key ID: A1B65B603268116B
319 changed files with 125222 additions and 0 deletions

2
public-class-repo/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
# Emacs temp files
*~

View file

@ -0,0 +1,319 @@
# Homework 1: OCaml introduction: functions, lists, tuples
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Monday, January 30 at 5:00pm
## Introduction
Below, you are asked to write a number of OCaml functions. Some are
simple, such as a function to determine if an integer input is even or
not. Others are more interesting and ask you to compute the square
root of a floating point number to a specified degree of accuracy.
Designing and implementing these functions will give you the
opportunity to test your knowledge of OCaml and how to write recursive
functions in it. Successfully completing these will set you up for
the more advanced (and more interesting) topics covered in this
course.
Recall that while some labs may be done collaboratively, *this work
is meant to be done on your own.*
## Designing and implementing functions in OCaml
All functions should be placed in a file named ``hwk_01.ml`` which
resides in a directory named ``Hwk_01``. This directory should be in
your GitHub repository.
In implementing these functions, do not use any functions from the
``List`` module. If you need some helper functions over lists, write
them yourself.
Also, you should only use the pure, non-imperative features of OCaml.
No while-loops or references. But since we've not discussed these
they are easy to avoid.
### even or odd
Write an OCaml function named ``even`` with the type ``int -> bool``
that returns ``true`` if its input is even, ``false`` otherwise.
Recall that we used the OCaml infix operator ``mod`` in class. You
may find it useful here.
Some example evaluations:
+ ``even 4`` evaluates to ``true``
+ ``even 5`` evaluates to ``false``
### Another GCD, Euclid's algorithmm
In class we wrote a greatest common divisor function that computed the
GCD of two positive integers by counting down by 1 from an initial
value that was greater than or equal to the GCD until we reached a
common divisor.
You are now asked to write another GCD function that is both simpler
to write and faster.
This one is based on the following observations:
+ gcd(a,b) = a, if a = b
+ gcd(a,b) = gcd(a, b-a), if a<b
+ gcd(a,b) = gcd(a-b,b) if a>b
This function should be named ``euclid`` and have the type ``int ->
int -> int``.
To get full credit on this problem, your solution must be based on the
observations listed above.
Some example evaluations:
+ ``euclid 6 9`` evaluates to ``3``
+ ``euclid 5 9`` evaluates to ``1``
### Adding and multiplying fractions
We can use OCaml's tuples to represent fractions as a pair of
integers. For example, the value ``(1,2)`` of type ``int * int``
represents the value one-half; ``(5,8)`` represents the value
five-eighths.
Consider the following function for multiplying two fractions
```
let frac_mul (n1,d1) (n2,d2) = (n1 *n2, d1 * d2)
```
It has type ``int * int -> int * int -> int * int``.
The expression ``frac_mul (1,2) (1,3)`` evaluates to ``(1,6)``.
Now write a function named ``frac_add`` that adds two fractions. It
should have the same type as our addition function: ``(int * int) ->
(int * int) -> (int * int)``.
You may assume that the denominator of any fraction is never 0.
Some example evaluations:
+ ``frac_add (1,2) (1,3)`` evaluates to ``(5,6)``
+ ``frac_add (1,4) (1,4)`` evaluates to ``(8,16)``
We see here that your addition function need not simplify fractions,
that is the job of the next function.
### Simplifiying fractions
Write another fraction function that simplifies fractions. It should
be called
``frac_simplify`` with type ``(int * int) -> (int * int)``.
Consider the following sample evaluations:
+ ``frac_simplify (8,16)`` evaluates to ``(1,2)``
+ ``frac_simplify (4,9)`` evaluates to ``(4,9)``
+ ``frac_simplify (3,9)`` evaluates to ``(1,3)``
As before, you may assume that the denominator is never 0.
You may want to use your ``euclid`` function in writing ``frac_simplify``.
### Square root approximation
Consider the following algorithms written in psuedo-code similar to C.
Assume that the "input" value ``n`` is greater than 1.0 and all
variables hold real numbers.
```
lower = 1.0;
upper = n;
accuracy = 0.001;
while ( (upper-lower) > accuracy ) {
guess = (lower + upper) / 2.0;
if ( (guess*guess) > n)
upper = guess;
else
lower = guess;
}
```
After this algorithm terminates we know that
*upper >= sqrt(n) >= lower* and
*upper - lower <= accuracy*.
That is, lower and upper proivde a bound on the actual square root of
n and that this bound is within the specified accuracy.
You are asked to write a function named ``square_approx`` with type
``float -> float -> (float * float)`` that implements the above
imperative algorithm, returning a pair of values corresponding to
``lower`` and ``upper`` in the imperative psuedo-code.
The first argument corresponds to ``n``, the value of which
we want to take the square root, and the second corresponds
to ``accuracy``.
Of course, this should be a recursive function that does not use any
of OCaml's imperative features such as while-loops and references.
Consider the gcd function that we wrote in class since it has some
characteristics that are similar to those needed for this function -
namely the need to carry additional changing values along the chain of
recursive function calls as additional parameters.
Consider the following sample evaluations:
+ ``square_approx 9.0 0.001`` evaluates to ``(3.,3.0009765625)``
+ ``square_approx 81.0 0.1`` evaluates to ``(8.96875,9.046875)``
(Small round off errors of floating point values are acceptable of
course.)
### Maximum in a list
Write a function ``max_list`` that takes a list of integers as input
and returns the maximum.
This function should have the type ``int list -> int``.
In your solution, write a comment that specifies any restrictions on
the lists that can be passed as input to your function.
Some sample interactions:
+ ``max_list [1; 2; 5; 3; 2]`` evaluates to ``5``
+ ``max_list [-1; -2; -5; -3; -2]`` evaluates to ``-1``
### Dropping list elements
Write another list processing function called ``drop`` with type ``int -> 'a list -> 'a list`` that drops a specified number of elements
from the input list.
For example, consider these evaluations:
+ ``drop 3 [1; 2; 3; 4; 5]`` evaluates to ``[4; 5]``
+ ``drop 5 ["A"; "B"; "C"]`` evaluates to ``[ ]``
+ ``drop 0 [1]`` evaluates to ``[1]``
You may assume that only non-negative numbers will be passed as the
first argument to ``drop``.
### List reverse
Write a function named ``rev`` that takes a list and returns the
reverse of that list.
Recall that ``@`` is the list append operator, you may find this useful.
Some sample interactions:
+ ``rev [1; 2; 3; 4; 5]`` evaluates to ``[5; 4; 3; 2; 1]``
+ ``rev []`` evaluates to ``[]``
### Closed polygon perimeter
This final problem asks you to compute the perimeter of a closed polygon
represented by a list of points.
You may assume that the list contains at least 3 elements (though our
solution only requires that the list be non-empty). You may also
assume that drawing line segments between each successive pair of
points leads to a closed polygon with no crossing lines.
Your function should be named ``perimeter`` and have the type
``(float * float) list -> float``.
This function is similar to ``sum_diffs`` from Lab 02 in that we apply
some function to each successive pair of points. In this case that
function is ``distance`` (also from Lab 02) instead of integer
subtraction.
But we must also include the distance between the first point in the
list and the last point in the list. So when our recursive function
gets to the base case of having just one more point in the list, it
must have access to the first point in the list so that we can return
the distance between the first and last points.
You will likely need to write a helper function, in a let-expression
nested in your definition of ``perimeter`` that carries along the
value of the first point until it is needed.
Recall how, in our GCD function, we carried along the value of the
potential GCD value that was decremented in each recursive call. You
will need to do something similar here; the only difference being that
the "carried along" value doesn't change with each call to the
recursive function.
A sample interaction:
+ ``perimeter [ (1.0, 1.0); (1.0, 3.0); (4.0, 4.0); (7.0, 3.0); (7.0, 1.0) ]``
evalutes to, roughly ``16.32``
### Representing matrices as lists of lists
We could consider representing matrices as lists of lists of numbers.
For example the list ``[ [1; 2; 3] ; [4; 5; 6] ]`` might represent a
matrix with two rows (each row corresponding to one of the "inner"
lists) and three columns.
Here the type is ``int list list`` - a list of integer lists.
Of course, the type allows for values that do not correspond to
matrices. For example, ``[ [1; 2; 3] ; [4; 5] ]`` would not represent
a matrix since the first "row" has 3 elements and the second has only
2.
Write a function ``is_matrix`` that takes in values such as the list
of lists given above and returns a boolean value of ``true`` if the
list of lists represents a proper matrix and ``false`` otherwise.
This function checks that all the "inner" lists have the same length.
Since you are not to use any library functions you need to write your
own function to determine the length of a list.
Some sample interactions:
+ ``is_matrix [ [1;2;3]; [4;5;6] ]`` evaluates to ``true``
+ ``is_matrix [ [1;2;3]; [4;6] ]`` evaluates to ``false``
+ ``is_matrix [ [1] ]`` evaluates to ``true``
### A simple matrix operation: matrix scalar addition
Write a function, ``matrix_scalar_add`` with type ``int list list ->
int -> int list list`` that implements matrix scalar addition. This
is simply the operation of adding the integer value to each element of
the matrix.
For example,
+ ``matrix_scalar_add [ [1; 2 ;3]; [4; 5; 6] ] 5`` evaluates to
``[ [6; 7; 8]; [9; 10; 11] ]``
You may assume that only matrices for which ``is_matrix`` evaluates to
``true`` are passed to this function.
## Bonus round
For a small number of extra credit points implement a matrix transpose
function named ``matrix_transpose`` that has type ``'a list list -> 'a
list list``. It should transpose a matrix such as ``[ [1; 2; 3]; [4;
5; 6] ]`` into ``[ [1; 4]; [2; 5]; [3; 6] ]``.
If you're feeling ambitious, try a matrix multiply function as well.
To simplify this, we'll assume that matrices hold integers and thus
your ``matrix_multiply`` function should have type ``int list list ->
int list list -> int list list``.

View file

@ -0,0 +1,390 @@
# Homework 2: Working with higher order functions.
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, February 17 at 5:00pm
Lab 4 on February 7 and Lab 5 on February 14 will be dedicated to
answering questions about this assignment and to providing
clarifications if any are needed.
Note that for this assignment you are not to write **any**
recursive functions. Further information on this restriction is
detailed in Part 3 of the assignment.
## Corrections to mistakes in original specification
+ The type of ``convert_to_non_blank_lines_of_words`` shoud be ``char list -> line list`` not ``string -> line list``.
## Introduction - The Paradelle
In this homework assignment you will write an OCaml program that
reads a text file and reports if it contains a poem that fits the
"fixed-form" style known as a *paradelle*.
Below is a sample paradelle called "Paradelle for Susan" by Billy
Collins from his book *Picnic, Lightning*.
> I remember the quick, nervous bird of your love. <br/>
> I remember the quick, nervous bird of your love. <br/>
> Always perched on the thinnest, highest branch. <br/>
> Always perched on the thinnest, highest branch. <br/>
> Thinnest of love, remember the quick branch. <br/>
> Always nervous, I perched on your highest bird the.
>
> It is time for me to cross the mountain. <br/>
> It is time for me to cross the mountain. <br/>
> And find another shore to darken with my pain. <br/>
> And find another shore to darken with my pain. <br/>
> Another pain for me to darken the mountain. <br/>
> And find the time, cross my shore, to with it is to.
>
> The weather warm, the handwriting familiar. <br/>
> The weather warm, the handwriting familiar. <br/>
> Your letter flies from my hand into the waters below. <br/>
> Your letter flies from my hand into the waters below. <br/>
> The familiar waters below my warm hand. <br/>
> Into handwriting your weather flies your letter the from the.
>
> I always cross the highest letter, the thinnest bird. <br/>
> Below the waters of my warm familiar pain, <br/>
> Another hand to remember your handwriting. <br/>
> The weather perched for me on the shore. <br/>
> Quick, your nervous branch flies for love. <br/>
> Darken the mountain, time and find my into it with from to to is.
Following this poem, Collins provides the following description of this form:
> The paradelle is one of the more demanding French fixed forms, first
appearing in the *langue d'oc* love poetry of the eleventh century. It
is a poem of four six-line stanzas in which the first and second lines,
as well as the third and fourth lines of the first three stanzas, must
be identical. The fifth and sixth lines, which traditionally resolve
these stanzas, must use *all* the words from the preceding
lines and *only* those words. Similarly, the final stanza must
use *every* word from *all* the preceding stanzas and
*only* those words.
Collins is actually being satirical here and poking fun at overly
rigid fixed-form styles of poetry. There is actually no form known as
the *paradelle*. This did not stop people from going off and trying
to write their own however. In fact, the above poem is slightly
modified from his original so that it actually conforms to the rules
of a paradelle.
To write an OCaml program to detect if a text file contains a paradelle
we add some more specific requirements to Collin's description above.
You should take these into consideration when completing this
assignment:
+ Blank lines are allowed, but we will assume that blank lines
consist of only a single newline ``'\n'`` character.
+ Punctuation and spacing (tabs and the space characters) should
not affect the comparison of lines in a stanza. For example, the
following two lines would be considered as "identical" because the
same words are used in the same order even though spacing and
punctuation are different.
``"And find the time,cross my shore, to with it is to"``
``"And find the time , cross my shore, to with it is to ."``
Thus, we will want to ignore punctuation symbols to some extent,
being careful to notice that they can separate words as in ``"time,cross"``.
Specifically, the punctuation we will
consider are the following :
``. ! ? , ; : -``
Other punctuation symbols will not be used in any input to assess
your program.
+ Also, we will need to split lines in the file (of Ocaml type
``string``) into a list of lines
and then split each line individual line into a list of
words. In the list of words there
should be no spaces, tabs, or punctuation symbols. Then we can
compare lists of words.
+ Capitalization does not matter. The words ``"Thinnest"``
and "``thinnest"`` are to be considered as the same.
+ In checking criteria for an individual stanza, each instance of
a word is counted. But in checking that the final stanza uses all
the words of the first 3, duplicate words should be removed.
That is, in checking that two lines "use the same words" we must
check that each word is used the same number of times in each line.
In checking that the final stanza uses all (and only) words from the
first 3 stanza, we do not care about how many times a word is
used. So if a word is used 4 times in the first 3 stanzas, it need
not be used 4 times in the final stanza.
+ Your program must return a correct answer for any text file.
For example, your program should report that an empty file or a file
containing a single character or the source code for this assignment
are not in the form of a paradelle.
## Getting started
Copy the contexts of the ``Homework/Hwk_02`` directory from the public
class repository into a ``Hwk_02`` directory in your individual
repository.
This file ``hwk_02.ml`` contains some helper functions that we'll use
in this assignment. The remainder are sample files containing
paradelles or text that is not a paradelle. The file names should
make this all clear.
## Part 1. Some useful functions.
Your first step is to define these functions that will be useful in
solving the paradelle check. Place this near the top of the
``hwk_02.ml`` file, just after the comment that says
```
(* Place part 1 functions 'take', 'drop', 'length', 'rev',
'is_elem_by', 'is_elem', 'dedup', and 'split_by' here. *)
```
### a length function, ``length``
Write a function, named ``length`` that, as you would expect, takes a
list and returns its length as a value of type ``int``
Annotate your function with types or add a comment
indicating the type of the function.
### list reverse ``rev``
Complete the definition of the reverse function ``rev`` in
``hwk_02.ml``. Currently is just raises an exception. Remove this
and replace the body with an expression that uses List.fold_left
or List.fold_right to do the work of reversing the list.
### list membership ``is_elem_by`` and ``is_elem``
Define a function ``is_elem_by`` which has the type
```
('a -> 'b -> bool) -> 'b -> 'a list -> bool
```
The first argument is a function to check if an element in the list
(the third argument) matches the values of the second argument. It
will return ``true`` if any element in the list "matches" (based on
what the first argument determines) an element in the list.
For example, both
```
is_elem_by (=) 4 [1; 2; 3; 4; 5; 6; 7]
```
and
```is_elem_by (fun c i -> Char.code c = i) 99 ['a'; 'b'; 'c'; 'd']``
evaluate to true.
Next, define a function ``is_elem`` whose first argument is a value and second
argument is a list of values of the same type. The function returns
``true`` if the value is in the list.
For example, ``is_elem 4 [1; 2; 3; 4; 5; 6; 7]`` should evaluate to
``true`` while ``is_elem 4 [1; 2; 3; 5; 6; 7]`` and ``is_elem 4 [ ]``
should both evaluate to ``false``.
``is_elem`` should be be implemented by calling ``is_elem_by``.
Annotate both of your functions with type information on the arguments
and for the result type.
### removing duplicates from a list, ``dedup``
Write a function named ``dedup`` that takes a list and removes all
duplicates from the list. The order of list elements returned is up
to you. This can be done with only a call to ``List.fold_right``,
providing you pass it the correct function that can be used to fold a
list up into one without any duplicate elements.
### a splitting function, ``split_by``
Write a splitting function named ``split_by`` that takes three arguments
1. an equality checking function that takes two values
and returns a value of type ``bool``,
2. a list of values that are to be separated,
3. and a list of separators values.
This function will split the second list into a list of lists. If the
checking function indicates that an element of the first list
(the second argument) is an element of the second list (the third
argument) then that element indicates that the list should be split at
that point. Note that this "splitting element" does not appear
in any list in the output list of lists.
For example,
+ ``split_by (=) [1;2;3;4;5;6;7;8;9;10;11] [3;7]`` should evaluate to
``[ [1;2]; [4;5;6]; [8;9;10;11] ]`` and
+ ``split_by (=) [1;2;3;3;3;4;5;6;7;7;7;8;9;10;11] [3;7]`` should
evaluate to ``[[1; 2]; []; []; [4; 5; 6]; []; []; [8; 9; 10; 11]]``.
Note the empty lists. These are the list that occur between the 3's
and 7's.
+ ``split_by (=) ["A"; "B"; "C"; "D"] ["E"]`` should evaluate to
``[["A"; "B"; "C"; "D"]]``
Annotate your function with types.
Also add a comment explaining the behavior of your function and its
type. Try to write this function so that the type is as general as
possible.
## Reading file contents.
Notice the provide helper functions ``read_chars`` and ``read_file``.
The second will read a file and return the list of characters, wrapped
up in an ``option`` type if it finds the file. If the file, with the
name passed to the function, can't be found, it will return ``None``.
## Part 2. Preparing text for the paradelle check.
The poems that we aim to check are stored as values of type ``string``
in text files. But the ``read_file`` function above will return this
data in a value of type ``char list option``.
We will need to break the input into a list of lines of text, removing
the blank lines, and also splitting the lines of text into lists of
words.
We need to write a function called
``convert_to_non_blank_lines_of_words`` that takes as input the poem
as an OCaml ``char list`` and returns a list of lines, where each line is
a list of words, and each word is a list of characters.
Thus, ``convert_to_non_blank_lines_of_words`` can be seen as having
the type ``char list -> char list list list``.
We can use the type system to name new types that make this type
easier to read.
First define the type ``word`` to be ``char list`` by
```
type word = char list
```
Then define a ``line`` type to be a ``word list``.
Then, we can specify that
``convert_to_non_blank_lines_of_words`` has
the type ``char list -> line list``.
In writing ``convert_to_non_blank_lines_of_words`` you may want to
consider a helper function that breaks up a ``char
list`` into lines, separated by new line characters (``'\n'``) and
another that breaks up lines into lists of words.
At this point you are not required to directly address the problems
relating to capitalization of letters which we eventually need to
address in checking that the same words appear in various parts of the
poem. You are also not required to deal with issues of punctuation,
but you may need to do something the be sure that words are correctly
separated. For example, we would want to see ``that,barn`` as two
words.
## Part 3. The paradelle check.
We will now need to consider how punctuation is to be handled, how
words are to be compared and, in the comparisons of lines, when
duplicate words should be dropped and when they should not be.
We can now begin to write the function to check that a poem is a
"paradelle".
To do this, write a function named ``paradelle`` that takes as input a
filename (a ``string``) of a file containing a potential paradelle.
This function then returns a value of the following type:
```
type result = OK
| FileNotFound of string
| IncorrectNumLines of int
| IncorrectLines of (int * int) list
| IncorrectLastStanza
```
This type describes the possible outcomes of the analysis. For example,
1. ``OK``- The file contains a paradelle.
1. ``FileNotFound "test.txt"`` - The file ``test.txt`` was not found.
1. ``IncorrectNumLines 18`` - The file contained 18 lines after the
blank lines were removed. A paradelle must have 24 lines.
1. ``IncorrectLines [ (1,2); (11,12) ]`` - Lines 1 and 2 are not the
same and thus this is not a paradelle. Also lines 11 and 12, in the
second stanza, do not have the same words as in the first 4 lines
of that stanza, and
this is another reason why this one is not a paradelle.
1. ``IncorrectLastStanza`` - the last stanza does not properly contain
the words from the first three stanzas.
**Remember, you are not to write any recursive functions.** Only
``read_chars``, ``take``, and ``drop`` can be used.
Furthermore, below is a list of functions from various OCaml modules
that you may also use. Functions not in this list may not be used.
(Except for functions such as ``input_char`` in functions that were
given to you.)
+ List.map, List.filter, List.fold_left, List.fold_right
+ List.sort, List.concat,
+ Char.lowercase, Char.uppercase
+ string_of_int
The ``sort`` function takes comparison functions as its first argument.
We saw how such functions are written and used in lecture.
These restrictions are in place so that you can see how interesting
computations can be specified using the common idioms of mapping,
filtering, and folding lists. The goal of this assignment is not
simply to get the paradelle checker to work, but to get it to work and
for you to understand how these higher order functions can be used.
## Some advice.
You will want to get started on this assignment sooner rather than
later. There are many aspects that you need to think about. Most
importantly is the structure of your program the various helper
functions that you may want to use.
We recommend writing your helper functions at the "top level" instead
of nested in a ``let`` expression so that you can inspect the type
inferred for them by OCaml and also run them on sample input to check
that they are correct.
## Feedback tests.
Feedback tests are not initially turned on. You should read these
specifications and make an effort to understand them based on the
descriptions.
If you have questions, ask your TAs in lab or post them to the "Hwk
02" forum on Moodle.
Feedback tests will be available next week.

View file

@ -0,0 +1,43 @@
(* This file contains a few helper functions and type declarations
that are to be used in Homework 2. *)
(* Place part 1 functions 'take', 'drop', 'length', 'rev',
'is_elem_by', 'is_elem', 'dedup', and 'split_by' here. *)
let rec take n l = match l with
| [] -> []
| x::xs -> if n > 0 then x::take (n-1) xs else []
let rec drop n l = match l with
| [] -> []
| x::xs -> if n > 0 then drop (n-1) xs else l
let rev lst = raise (Failure "This function is not yet implemented!")
(* Some functions for reading files. *)
let read_file (filename:string) : char list option =
let rec read_chars channel sofar =
try
let ch = input_char channel
in read_chars channel (ch :: sofar)
with
| _ -> sofar
in
try
let channel = open_in filename
in
let chars_in_reverse = read_chars channel []
in Some (rev chars_in_reverse)
with
_ -> None
type result = OK
| FileNotFound of string
| IncorrectNumLines of int
| IncorrectLines of (int * int) list
| IncorrectLastStanza

View file

@ -0,0 +1,27 @@
When Emma scrunches up her nose and knits her tiny brow,
When Emma scrunches up her nose and knits her tiny brow,
My granddaughter spins a happy web of hyphens that connect-her-eyes.
My granddaughter spins a happy web of hyphens that connect-her-eyes.
Connect her up, her brow, her nose, a web of Emma scrunches
That, when granddaughter knits, spins tiny hyphens and my happy eyes.
But big-spring-sky-blues get old too fast, and early some time near
But big-spring-sky-blues get old too fast, and early some time near
Will dull as she forgets slow what we've already lost.
Will dull as she forgets slow what we've already lost.
As sky already forgets spring, we've but dull old blues, slow, fast
And near, some big time. What, she will get lost early, too.
Her tousled-angel-twinkly-pouts accuse her hovering tutors.
Her tousled-angel-twinkly-pouts accuse her hovering tutors.
Her wise eyes smirk: here's to whatever we the grownups might recall.
Her wise eyes smirk: here's to whatever we the grownups might recall.
To angel eyes, we hovering grownups, smirk wise tutors, accuse:
Here's her whatever, her tousled recall, her twinkly might, the pouts.
When old tutors smirk pouts, we've tousled her twinkly times.
The wise get fast too early and slow her will some,
And as granddaughter knits up that tiny nose, spins her brow of scrunches,
She already near lost her might. Here's what big dull sky forgets:
Spring blues, her happy eyes, her hyphens -------- , a web.
But recall, my Emma, hovering angel eyes, connect to grownups, whatever we accuse.

View file

@ -0,0 +1,27 @@
I remember the quick, nervous bird of your love.
I remember the, nervous bird of your love.
Always perched on the thinnest, highest branch.
Always perched on the thinnest, highest branch.
Thinnest of love, remember the quick branch.
Always nervous, I perched on your highest bird the.
It is time for me to cross the mountain.
It is time for me to cross the mountain.
And find another shore to darken with my pain.
And find another shore to darken with my pain.
Another pain for me to darken the mountain.
And find the time, cross my shore, to with it is to.
The weather warm, the handwriting familiar.
The weather warm, the handwriting familiar.
Your letter flies from my hand into the waters below.
Your letter flies from my hand into the waters below.
The familiar waters below my warm hand.
Into handwriting your weather flies your letter the from the.
I always cross the highest letter, the thinnest bird.
Below the waters of my warm familiar pain,
Another hand to remember your handwriting.
The weather perched for me on the shore.
Quick, your nervous branch flies for love.
Darken the mountain, time and find my into it with from to to is.

View file

@ -0,0 +1,27 @@
I remember the quick, nervous bird of your love.
I remember the quick, nervous bird of your love.
Always perched on the thinnest, highest branch.
Always perched on the thinnest, highest branch.
Thinnest of love, remember the quick branch.
Always nervous, I perched on your highest bird the.
It is time for me to cross the mountain.
It is time for me to cross the mountain.
And find another shore to darken with my pain.
And find another shore to darken with my pain.
Another pain for me to darken the mountain.
And find the time, cross my shore, to with it is to.
The weather warm, the handwriting familiar.
The weather warm, the handwriting familiar.
Your letter flies from my hand into the waters below.
Your letter flies from my hand into the waters below.
The familiar waters below my warm hand.
Into handwriting your weather flies your letter the from the.
I always cross the highest letter, the thinnest bird.
Below the waters of my warm familiar pain,
Another hand to remember your handwriting.
The weather perched for me on the shore.
Quick, your nervous branch flies for love.
Darken the mountain, time and find my into it with from to is.

View file

@ -0,0 +1,27 @@
I remember the, nervous bird of your love.
I remember the quick, nervous bird of your love.
Always perched on the thinnest, highest branch.
Always perched on the thinnest, highest branch.
Thinnest of love, remember the quick branch.
Always nervous, I perched on your highest bird the.
It is time for me to cross the mountain.
It is time for me to cross the mountain.
And find another shore to darken with my pain.
And find another shore to darken with my pain.
Another pain for me to darken the mountain.
And find the time, cross my shore, to with it is to.
The weather warm, the handwriting familiar.
The weather warm, the handwriting familiar.
Your letter flies from my hand into the waters below.
Your letter flies from my hand into the waters below.
The familiar waters below my warm hand.
Into handwriting your weather flies your letter the from the.
I always cross the highest letter, the thinnest bird.
Below the waters of my warm familiar pain,
Another hand to remember your handwriting.
The weather perched for me on the shore.
Quick, your nervous branch flies for love.
Darken the mountain, time and find my into it with from to to is.

View file

@ -0,0 +1,11 @@
When Emma scrunches up her nose and knits her tiny brow,
When Emma scrunches up her nose and knits her tiny brow,
My granddaughter spins a happy web of hyphens that connect-her-eyes.
My granddaughter spins a happy web of hyphens that connect-her-eyes.
Connect her up, her brow, her nose, a web of Emma scrunches
That, when granddaughter knits, spins tiny hyphens and my happy eyes.
But big-spring-sky-blues get old too fast, and early some time near
But big-spring-sky-blues get old too fast, and early some time near
Will dull as she forgets slow what we've already lost.

View file

@ -0,0 +1,27 @@
When Emma scrunches up her nose and knits her tiny brow,
When Emma scrunches up her nose and knits her tiny brow,
My granddaughter spins a happy web of hyphens that connect-her-eyes.
My granddaughter spins a happy web of hyphens that connect-her-eyes.
Connect her up, her brow, her nose, a web of Emma scrunches
That, when granddaughter knits, spins tiny hyphens and my happy eyes.
But big-spring-sky-blues get old too fast, and early some time near
But big-spring-sky-blues get old too fast, and early some time near
Will dull as she forgets slow what we've already lost.
Will dull as she forgets slow what we've already lost.
As sky already forgets spring, we've but dull old blues, slow, fast
And near, some big time. What, she will get lost early, too.
Her tousled-angel-twinkly-pouts accuse her hovering tutors.
Her tousled-angel-twinkly-pouts accuse her hovering tutors.
Her wise eyes smirk: here's to whatever we the grownups might recall.
Her wise eyes smirk: here's to whatever we the grownups might recall.
To angel eyes, we hovering grownups, smirk wise tutors, accuse:
Here's her whatever, her tousled recall, her twinkly might, the pouts.
When old tutors smirk pouts, we've tousled her twinkly time.
The wise get fast too early and slow her will some,
And as granddaughter knits up that tiny nose, spins her brow of scrunches,
She already near lost her might. Here's what big dull sky forgets:
Spring blues, her happy eyes, her hyphens -------- , a web.
But recall, my Emma, hovering angel eyes, connect to grownups, whatever we accuse.

View file

@ -0,0 +1,27 @@
I remember the quick, nervous bird of your love.
I remember the quick, nervous bird of your love.
Always perched on the thinnest, highest branch.
Always perched on the thinnest, highest branch.
Thinnest of love, remember the quick branch.
Always nervous, I perched on your highest bird the.
It is time for me to cross the mountain.
It is time for me to cross the mountain.
And find another shore to darken with my pain.
And find another shore to darken with my pain.
Another pain for me to darken the mountain.
And find time, cross my shore, to with it is.
The weather warm, the handwriting familiar.
The weather warm, the handwriting familiar.
Your letter flies from my hand into the waters below.
Your letter flies from my hand into the waters below.
The familiar waters below my warm hand.
Into handwriting, weather flies your letter the from the.
I always cross the highest letter, the thinnest bird.
Below the waters of my warm familiar pain,
Another hand to remember your handwriting.
The weather perched for me on the shore.
Quick, your nervous branch flies for love.
Darken the mountain, time and find my into it with from to to is.

View file

@ -0,0 +1,31 @@
I remember the quick, nervous bird of your love.
I remember the quick, nervous bird of your love.
Always perched on the thinnest, highest branch.
Always perched on the thinnest,highest branch.
Thinnest of love, remember the quick branch.
Always nervous, I perched on your highest bird the.
It is time for me to Cross the mountain.
It is time for me to cross the mountain.
And find another shore to darken with my pain.
And find another shore to darken with my pain.
Another pain for me to darken the mountain.
And find time, cross my shore, to with it is.
The weather warm, the handwriting familiar.
The weather warm, the handwriting familiar.
Your letter flies from my hand into the waters below.
Your letter flies from my hand into the waters below.
The familiar waters below my warm hand.
Into handwriting, weather flies your letter the from the.
I always cross the highest letter, the thinnest bird.
Below the waters of my warm familiar pain,
Another hand to remember your handwriting.
The weather perched for me on the shore.
Quick, your nervous branch flies for love.
Darken the mountain, time and find my into it with from to to is.

View file

@ -0,0 +1,4 @@
## Homework 3.
See the PDF file ``Hwk_03.pdf`` for instruction on homework 3.

Binary file not shown.

View file

@ -0,0 +1,517 @@
# Homework 4: Programs as Data.
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Wednesday, March 22 at 5:00pm.
## Introduction
In this assignment you'll write a few functions that work with
inductive values representing expressions for a small subset of
OCaml.
In part 1 your functions will convert arithmetic expressions
into strings, in an easy way and then in a way that doesn't generate
unnecessary parenthesis.
In part 2 you'll write an evaluation function the computes the value
of an expression. But these expressions may define and use recursive
functions so they are computationally interesting.
I expect that this assignment may take up to 10 or 12 hours to
complete, if not more. So you should start early and not wait until
after spring break to begin. Be sure to get the help you may need
from TAs before spring break.
There is plenty of time to do this work if you start now. The due
date will not be pushed back.
## Getting started
## Part 1 - arithmetic expressions as strings
To begin this component, copy the file ``arithmetic.ml`` from
the directory ``Homework/Hwk_04`` in the public class repository and
place it in a directory named ``Hwk_04`` in your repository.
### Simple "unparsing"
Consider the following type for expressions. We've used variants of
this in class for a number of examples:
```
type expr
= Const of int
| Add of expr * expr
| Mul of expr * expr
| Sub of expr * expr
| Div of expr * expr
```
This type definition can be found in ``arithmetic.ml``.
Write a function named ``show_expr`` that converts an ``expr`` value
into a ``string`` representation using traditional infix symbols for
the operators. The output of this function should be something you
could copy and paste into the OCaml interpreter and have it evaluate
to the expected value.
Addition, multiplication, subtraction, and division
should be wrapped in parenthesis so that the generated string
represents the same expression as the input ``expr`` value.
Constants, however, should not be wrapped in parenthesis.
Your function must include type annotations indicate the types of the
arguments and the return type.
Here are some example evaluations of ``show_expr``:
+ ``show_expr (Add(Const 1, Const 3))`` evaluates to ``"(1+3)"``
+ ``show_expr (Add (Const 1, Mul (Const 3, Const 4)))`` evaluates to ``"(1+(3*4))"``
+ ``show_expr (Mul (Add(Const 1, Const 3), Div(Const 8, Const 4)))`` evaluates to ``"((1+3)*(8/4))"``
### Pretty-printing expressions
While the function ``show_expr`` should create legal expression
strings, they often have extra parenthesis that are not needed to
understand the meaning of the expression. This
problem asks you to write a similar function named
``show_pretty_expr`` that does not create any unnecessary parenthesis.
A pair of parenthesis, that is a matching "(" and ")", in
a string representation of an expression are unnecessary if the value
of the expression with the parenthesis and the value of the expression
without the parenthesis are the same.
Consider the following example using ``show_expr``:
+ ``show_expr (Add (Const 1, Mul (Const 3, Const 4)))``
evaluates to ``"(1+(3*4))"``
The parenthesis around the product "3*4" are not necessary. Neither
are the parenthesis around the entire expression.
However, in the following case
+ ``show_expr (Mul (Const 4, Add (Const 3, Const 2)))``
evaluates to ``"(4*(3+2))"``
The inner parenthesis around ``3+2`` are needed. Again, the outer
parenthesis are not needed. This is because multiplication has higher
precedence than addition.
These examples illustrate that an expression needs to be wrapped
in parenthesis if its operator's precedence is lower than the
operator of the expression containing it.
We think of operator precedence as being equal to, lower than,
or higher than the precedence of other operators. This suggest that
we use integers to represent the precedence of different operators.
We must also consider the associativity of an operation. Consider this
``expr`` value and the result of applying ``show_expr`` to it.
+ ``show_expr (Sub (Sub (Const 1, Const 2), Sub (Const 3, Const 4)))``
evaluates to ``"((1-2)-(3-4))"``
Note that since subtraction is left associative (a value between two
subtraction operators is associated with the one to its left) the
parenthesis around ``1-2`` are not needed, but those around ``3-4``
are needed.
All operations represented in our ``expr`` type are left associative.
So when the enclosing operator has the same precedence, it may suffice
for an expression to know if it should be wrapped in parenthesis or
not, by knowing if it is the left child or right child (if either) of
the expression that it is a component of.
Of course in some cases, such as at the root of the expression, it
might not be a component of a binary operator.
Write a function ``show_pretty_expr`` that generates a ``string``
representation of an ``expr`` similar to ``show_expr`` but without any
unnecessary parenthesis. In doing so, write appropriate helper
functions to avoid an overabundance of copy-and-pasted code fragments
that are non-trivial near exact copies of one another.
Also, be sure to use disjoint union types where appropriate. While
integers are appropriate for representing precedence of operators,
they may not be appropriate in dealing with issues of associativity.
A few more sample evaluations:
+ ``show_pretty_expr (Add (Const 1, Mul (Const 3, Const 4)))``
evaluates to ``"1+3*4"``
+ ``show_pretty_expr (Add (Mul (Const 1, Const 3), Const 4))``
evaluates to ``"1*3+4"``
+ ``show_pretty_expr (Add (Const 1, Add (Const 3, Const 4)))``
evaluates to ``"1+(3+4)"``
+ ``show_pretty_expr (Add (Add (Const 1, Const 3), Const 4))``
evaluates to ``"1+3+4"``
+ ``show_pretty_expr (Mul (Const 4, Add (Const 3, Const 2)))``
evaluates to ``"4*(3+2)"``
+ ``show_pretty_expr (Sub (Sub (Const 1, Const 2), Sub (Const 3, Const 4)))``
evaluates to ``"1-2-(3-4)"``
It should be clear that ``show_pretty_expr`` cannot be a simple
recursive function with only an ``expr`` as input and a ``string`` as
output. Nested expressions will likely need to know what kind of
expression they are nested in, and maybe other information as well.
Thus ``show_pretty_expr`` will likely call another function that has
additional inputs that actually does the work of constructing the
string. Determining what this additional information is and how it is
passed around in the function calls is the interesting part of this
exercise.
## Part 2 - evaluation of expressions with functional values
In class we've written bits and pieces of evalutors for different
kinds of expressions with different kinds or representations for
values.
In this part of the assignment we pull all these pieces together to
build an interpreter for a small but computationally powerful subset
of OCaml.
For this part of the assignment you will implement a function named
``evaluate`` that has the type ``expr -> value``.
The type for ``expr`` is provided below and an incomplete
specification for the type ``value`` is also given.
```
type expr
= Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
| Lt of expr * expr
| Eq of expr * expr
| And of expr * expr
| If of expr * expr * expr
| Id of string
| Let of string * expr * expr
| LetRec of string * expr * expr
| App of expr * expr
| Lambda of string * expr
| Value of value
and value
= Int of int
| Bool of bool
| Closure of string * expr * environment
```
To begin this component, copy the file ``eval.ml`` from
the directory ``Homework/Hwk_04`` and place it in a directory named
``Hwk_04`` in your repository. That file has the type definitions
given above.
### Step 1 - determining the free variables in an expression
To get started with this richer form of expressions write a function
named ``freevars`` that has the type ``expr -> string list``.
This function will, as the name suggests, return the list of free
variables that appear in an expression. These are the names that are
not "bound" by a let-expression or a by a lambda-expression.
Note that we also have a "let-rec" expression that we will be using
for recursive functions. It is intended to have the same semantics as
the ``let rec`` construct in OCaml. For this constucts, the free
variables are those found in either of the two component expressions,
except that we don't include the name bound by the let-rec in the list
of names that are returned.
Consider these sample evaluations of a correct implementation of
``freevars``:
+ ``freevars (Add (Value (Int 3), Mul (Id "x", Id "y")))``
evaluates to ``["x"; "y"]``
+ ``freevars (Let ("x", Id "z", Add (Value (Int 3), Mul (Id "x", Id "y")))`)`
evaluates to ``["z"; "y"]``
+ ``freevars (Let ("x", Id "x", Add (Value (Int 3), Mul (Id "x", Id "y")))`)`
evaluates to ``["x"; "y"]``
+ ``freevars (Lambda ("x", Add (Value (Int 3), Mul (Id "x", Id "y"))))``
evaluates to ``["y"]``
+ ``freevars sumToN_expr``
evaluates to ``[]``
where ``sumToN_expr`` is as defined at the end of this file and in the
``eval.ml`` file you copied from the public repository.
### Step 2 - environments
The function that the tests will call must be named
``evaluate`` and must have the type ``expr -> value``. You will need
to use environments in this work in a manner similar to what we did in
some of the in-class examples. Thus you might write a helper function
called ``eval`` that can take any extra needed arguments to actually
perform the evaluation.
### Step 3 - arithmetic expressions
To get stared, first ensure that ``evaluate`` will work for the
arithmetic operations and integer constants. Much of the work for this
has been done in some of the in-class example already.
An example evaluation:
+ ``evaluate (Add (Value (Int 1), Mul (Value (Int 2), Value (Int 3))))``
evaluates to ``Int 7``
### Step 4 - logical and relational expressions
Logical and relational operations are also straightforward:
Some sample evaluations:
+ ``evaluate (Eq (Value (Int 1), Mul (Value (Int 2), Value (Int 3))))``
evaluates to ``Bool false``
+ ``evaluate (Lt (Value (Int 1), Mul (Value (Int 2), Value (Int 3))))``
evaluates to ``Bool true``
### Step 5 - conditional expressions
Conditional expressions should also pose not significant challenge.
For example
```
evaluate
(If (Lt (Value (Int 1), Mul (Value (Int 2), Value (Int 3))),
Value (Int 4),
Value (Int 5)))
```
evaluates to ``Int 4 ``
### Step 6 let expressions
We've implemented non-recursive let-expressions in a forms in
class. Adapting that work to this setting should be straightforward.
### Step 7 - non-recursive functions
We've spent some time in class discussing closures as the way to
represent the value of a lambda expression. The slides have several
examples of this, a few of which are reproduced here.
The values ``inc`` and ``add`` are defined as follows:
```
let inc = Lambda ("n", Add(Id "n", Value (Int 1)))
let add = Lambda ("x",
Lambda ("y", Add (Id "x", Id "y"))
)
```
Some sample evaluations:
+ ``evaluate inc``
evaluates to ``Closure ("n", Add (Id "n", Value (Int 1)), [])``
+ ``evaluate add``
evaluates to ``Closure ("x", Lambda ("y", Add (Id "x", Id "y")), [])``
+ ``evaluate (App (add, Value (Int 1)))``
evaluates to ``Closure ("y", Add (Id "x", Id "y"), [("x", Int 1)])``
+ ``evaluate (App ( (App (add, Value (Int 1))), Value (Int 2)))``
evaluates to ``Int 3``
### Step 8 - recursive functions
Consider the ``sumToN`` function we discussed in class. In OCaml,
we'd write this function as follows:
```
let rec sumToN = fun n ->
if n = 0 then 0 else n + sumToN (n-1)
in sumToN 4
```
To represent this function in our mini-OCaml language defined by the
``expr`` type, we'd represent the function as follows:
```
let sumToN_expr : expr =
LetRec ("sumToN",
Lambda ("n",
If (Eq (Id "n", Value (Int 0)),
Value (Int 0),
Add (Id "n",
App (Id "sumToN",
Sub (Id "n", Value (Int 1))
)
)
)
),
Id "sumToN"
)
```
Here we've given the name ``sumToN_expr`` to the ``expr`` value that
represent our ``sumToN`` function.
+ ``evaluate (App (sumToN_expr, Value (Int 10)))``
evaluates to ``Int 55``
### Handling ill-formed expressions
#### Type errors
Expressions, that is values of type ``expr``, do not necessarily
represent well-typed programs.
For example,
```
Add (Value (Int 4), Value (Bool true))
```
is type-correct OCaml code, but it represents an expression that is
not type-correct.
Your evaluation function should handle type errors like this by
raising an exception of type ``Failure`` whose string component has
the phrase "incompatible types".
For example
+ ``evaluate (Add (Value (Int 4), Value (Bool true)))``
raises the exception ``Exception: Failure "incompatible types, Add".``
In the reference solution, this is accomplished by evaluating this
expression:
```
raise (Failure "incompatible types on Add")
```
Your solution should generate messages for ``Failure`` exceptions that
have this form: "incompatible types on AAA" where AAA is replaced by
the
constructor name that is used in the type ``expr``
Similar exceptions should be raised for other type errors on other
constructors such as ``Mul``, ``Lt``, ``If``, etc.
#### Unbound identifiers
Expressions may also have free variable and evaluating these should
raise an exception as well.
For example,
+ ``evaluate (Add (Id "x", Value (Int 3)))``
should raise a ``Failure`` exception with the phrase "x not in
scope". If the name was "y" then the phrase should be "y not in
scope.
#### Recursive let expressions
Recursive let expressions should only bind names to lambda
expressions.
That is, ``expr`` values of the form
```
LetRec (_, Lambda (_, _), )
```
are valid, but a ``LetRec`` that does not have a ``Lambda`` as the
expression as its second component is considered invalid.
If this invalid form of expression is evaluated then your solution
should raise a ``Failure`` exception with the message containing the
phrase "let rec expressions must declare a function".
## Part 3 - Optional next steps
If you are interested in developing these ideas further you are
encouraged to attempt the following problems.
### Static error detection - type checking
Instead of raising an exception when an ill-formed expression, of the
type described above, we could instead do some so-called static
analysis of the expressions to detect the problems *before* evaluating
it.
One way to detect type errors is to do what is called type checking.
In this approach we would extend the ``Lambda``, ``Let``, and
``LetRec`` constructors so that some indication of the type of the
name being introduced is part of these constructors.
For example, we may indicate that a name ``x`` is to have integer type
in a let with the following:
```
Let ("x", IntType, Value (Int 4), Add (Id "x", Value (Int 5)))
```
Similar annotations would be needed for the other mentioned
constructors.
With this information in the representation of the expression can you
write a function the checks for the above mentioned forms of invalid
expressions without evaluating them?
What would this function return?
### Static error detection - type inference
How might we statically detect this kinds of errors but without adding
the type annotations described above. That is, using the existing
definition of `expr` can we detect possible type errors statically?
A correct solution to either of these static error detection problems
would give a guarantee that if no static errors are reported, then
during evaluation there will be no type errors, regardless of the
input.
### More interesting data structures
Can you extend ``expr`` and ``value`` to support lists and tuples as
they are found in OCaml?
Can you extend your type checking or type inference solutions to work
with these new constructs?
### Turning in your work
If you do want to try these problems, create a file (of files) for
your solution that is different from the ``eval.ml`` solution that
will be the basis of our assessment of the required parts of this
problem.
Any solution must come with significant documentation explaining what
it is that you've done and how your solution works.
Doing this work may improve your grade in this course, but there is no
guarantee of that. You attempt these problems because you find the
problems interesting and challenging - not because you want to improve
your grade.

View file

@ -0,0 +1,7 @@
type expr
= Const of int
| Add of expr * expr
| Mul of expr * expr
| Sub of expr * expr
| Div of expr * expr

View file

@ -0,0 +1,62 @@
type expr
= Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
| Lt of expr * expr
| Eq of expr * expr
| And of expr * expr
| If of expr * expr * expr
| Id of string
| Let of string * expr * expr
| LetRec of string * expr * expr
| App of expr * expr
| Lambda of string * expr
| Value of value
and value
= Int of int
| Bool of bool
| Closure of string * expr * environment
(* You may need an extra constructor for this type. *)
let evaluate (e:expr) : value =
raise (Failure "Complete this function...")
(* Some sample expressions *)
let inc = Lambda ("n", Add(Id "n", Value (Int 1)))
let add = Lambda ("x",
Lambda ("y", Add (Id "x", Id "y"))
)
(* The 'sumToN' function *)
let sumToN_expr : expr =
LetRec ("sumToN",
Lambda ("n",
If (Eq (Id "n", Value (Int 0)),
Value (Int 0),
Add (Id "n",
App (Id "sumToN",
Sub (Id "n", Value (Int 1))
)
)
)
),
Id "sumToN"
)
let twenty_one : value = evaluate (App (sumToN_expr, Value (Int 6)));

View file

@ -0,0 +1,317 @@
# Homework 5: Lazy Evaluation
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** 5pm Wednesday, April 5
## Part 1: Expression evaluation by-hand
In class, and demonstrated in the document "Expression Evaluation
Examples" on Moodle, we have evaluated expressions by hand, step by
step, to understand the different ways in which call by value
semantics, call by name semantics, and lazy evaluation work.
#### Question 1
Consider the following definitions:
```
sum [] = 0
sum x::xs -> x + sum xs
take 0 lst = [ ]
take n [ ] = [ ]
take n (x::xs) = x::take (n-1) xs
some_squares_from 0 v = [ ]
some_squares_from n v = v*v :: some_squares_from (n-1) (v+1)
```
Evaluate
```
sum (take 3 (some_squares_from 5 1))
```
using call by value semantics, call by name semantics, and lazy
evaluation.
Each of these three evaluations must be clearly labeled with the form
of evaluation used.
Furthermore, all must be produced electronically. No handwritten work
will be accepted. You may use a text editor and turn in a text file.
These are relatively simple so there is no need to use MarkDown or
generate a PDF file.
Your name and University Internet ID must appear in the upper left of
the document.
Name this file ``question_1.txt`` and place it in a directory named
``Hwk_05`` in your GitHub repository.
#### Question 2
Recall our definitions for ``foldl`` and ``foldr`` as well as the
functions for folding ``and`` over a list of boolean values. (Note
that we removed the underscores from the names as they appeared on the
slides.)
```
foldr f [] v = v
foldr f (x::xs) v = f x (foldr f xs v)
foldl f v [] = v
foldl f v (x::xs) = foldl f (f v x) xs
and b1 b2 = if b1 then b2 else false
andl l = foldl and true l
andr l = foldr and l true
```
You are asked to evaluate the expressions
```
andl (t::f::t::t::[])
```
and
```
andr (t::f::t::t::[])
```
using both call by value and call by name semantics.
A few things to note:
+ Since anytime a function uses an argument more than once
(this only happens with ``f`` in the two fold functions) it is given
a value and not an unevaluated expression, there is no benefit from
the optimization we get with lazy evaluation. So we don't need to
consider it here.
+ To save space we are not spelling out the full name of the values
``true`` and ``false`` but are instead abbreviating them here as
``t`` and ``f``. Do not consider these to be variables! They are
the boolean literals for true and false. You may do the same in
your homework.
+ Lists are written in their basic form using the cons (``::``) and
nil (``[]``) operators instead of the syntactic sugar form using
semicolons to separate lists values between square brackets.
Clearly label each evaluation with the kind of semantics used for it.
After you have completed all four of them, explain which one is the
most efficient and why.
Each of these three evaluations must be clearly labeled with the form
of evaluation used.
Furthermore, all must be produced electronically. No handwritten work
will be accepted. As before, you may use a text editor and turn in a
text file.
Your name and University Internet ID must appear in the upper left of
the document.
Name this file ``question_2.txt`` and place it
in the ``Hwk_05`` directory your created for question 1.
## Part 2: Efficiently computing the conjunction of a list of boolean values in OCaml
Write an OCaml function named ``ands`` with the type
``bool list -> bool`` that computes the same result as the
``andl`` and ``andr`` functions described in the problem above.
Your OCaml function should be written so that it does not examine or
traverse the entire list if it does not need to. We saw this behavior
in one of the by-hand evaluations above. If a ``false`` value is
encountered then it the computation can terminate and return
``false``.
This function should, following the pattern of past assignments, be
placed in a file named ``hwk_05.ml``. This file must be placed in a
directory named ``Hwk_05`` in your GitHub repository.
## Part 3: Implementing Streams in OCaml
In class, and demonstrated in the file ``streams.ml`` in the
code-examples directory of the public repositories, we developed a
type constructor ``stream`` that can be used to create lazy streams
and make use of lazy evaluation techniques in a strict/eager language.
Below you are asked to define a collection of stream values and
functions over streams.
To start this part of the assignment, first copy the ``streams.ml``
file into your ``Hwk_05`` directory. Add the following functions to
the end of that file. But you should clearly mark the parts of this
file that you did not write and attribute them to their author (your
instructor) and then indicate where your work starts in the file by
adding you name and a comment to this effect.
#### ``cubes_from``
Define a function ``cubes_from`` with the type ``int -> int stream``
that creates a stream of the cubes of numbers starting with the
input value. For example, ``cubes_from 5`` should return a stream
that contains the values 125, 216, 343, ...
Demonstrate to yourself that this work by using ``take`` to generate a
finite number of cubes.
#### ``drop``
Write a function named ``drop`` with the type ``int -> 'a stream -> 'a
stream``. This function is the stream version of the ``drop``
function that you've written for lists. Note the difference of the
type for ``drop`` for that of ``take`` over lists. Since ``take``
will remove a finite number of elements from a stream we have decided
to store them in a list instead.
#### ``drop_until``
Write a function named ``drop_until`` with the type
``('a -> bool) -> 'a stream -> 'a stream`` that returns the "tail" of
a stream after dropping all of the initial values for which the first
argument function returns ``false``.
That is, this function keeps dropping elements of the input stream
until it finds one for which the function returns ``true``. This
element, and all those that follow it, form the stream that is
returned.
For example, using ``head`` and ``squares`` from our original
``streams.ml`` file
```
head (drop_until (fun v -> v > 35) squares)
```
should evaluate to ``36``
#### ``map``
In ``streams.ml`` we defined the functions ``filter`` and ``zip`` to
work over lists. You are now asked to define a ``map`` function over
steams with the type ``('a -> 'b) -> 'a stream -> 'b stream``. This
is the analog to ``map`` over lists.
#### ``squares``, again.
In the class examples we defined the stream of squares of natural
numbers using ``zip`` so that ``squares = zip ( * ) nats nats``.
We could also define ``squares`` as ``squares_from 1`` using the
function defined above.
Define ``squares_again`` to be equal to ``squares`` but define it
using the ``map`` function written above.
#### square roots
You've previously seen a recursive function and an imperative loop to
compute the square root of a floating point number to some specified
degree of accuracy.
We now make use of lazy evaluation to pull apart two aspects of this
algorithm
1. the part the generates a sequence of approximations to
the square root of a number, and
2. the part that determines when the approximations generated so far
are close enough that we can stop generating them
The **first task** is to define the function ``sqrt_approximations`` with
the type ``float -> float stream`` that takes a floating point number
and generates the stream of approximations to its square root. This
sequence corresponds to the sequence of values that the local variable
``guess`` took in a previous implementation of this algorithm.
For example, ``take 10 (sqrt_approximations 49.0)`` evaluates to
```
[25.; 13.; 7.; 10.; 8.5; 7.75; 7.375; 7.1875; 7.09375; 7.046875]
```
(Recall that floating point values are approximations or real numbers
and thus there may be small round off errors that cause your results
to be slightly different from these.)
The **second task** is to write a function whose purpose is similar to
the ``accuracy`` value used in the previous implementations, that is,
to determine when to stop generating approximations.
This function should be named ``epsilon_diff`` with the type ``float
-> float stream -> float``. The first argument is a floating point
value, perhaps named ``epsilon``. This function pulls values out of the
stream until the difference between two successive values in the
stream is less than epsilon. It then returns the second of those two
values.
An example below uses a ``stream`` named ``diminishing`` which begins
at ``16.0`` and each following value is half of its preceding on.
For example, in utop:
```
# take 10 diminishing ;;
- : float list = [16.; 8.; 4.; 2.; 1.; 0.5; 0.25; 0.125; 0.0625; 0.03125]
```
Define ``diminishing`` in your file.
We can use ``epsilon_diff`` to as follows:
```
# epsilon_diff 0.3 diminishing ;;
- : float = 0.25
```
Since the difference between ``0.5`` and ``0.25`` is the first one
that is less than ``0.3``, the second of them is returned.
As a **third task**, include the following declarations to compute the
square root of ``50.0`` to different degrees of accuracy:
```
let rough_guess = epsilon_diff 1.0 (sqrt_approximations 50.0)
let precise_calculation = epsilon_diff 0.00001 (sqrt_approximations 50.0)
```
#### another square root
The function ``epsilon_diff`` looked at two successive values to
determine when to stop traversing a stream.
We now want to write a square root approximation function that picks
the first element out of ``sqrt_approximations`` that is within some
threshold. This one looks at the elements of ``sqrt_approximations``
separately instead of comparing two of them in determining what value
to return.
This function, named ``sqrt_threshold``, has the type ``float -> float
-> float``. The first floating point value is the one we would like
to take the square root of, call it ``v``. The second is a threshold
value, call it ``t``. We want return the first element, say ``s``, of
``sqrt_approximations`` for which the absolute value of ``(s *. s)
-. v`` is less than ``t``.
For full credit this function should make appropriate use of the
functions you've already written and those in the ``streams.ml`` file
we developed in class.
This function, at first glance, seems to return more accurate
answers than our value of epsilon might suggest. For example,
```
sqrt_threshold 50.0 3.0
```
evaluates to ``7.125``.
Write a comment in your OCaml file just above the definition of
``sqrt_threshold`` that explains why this is.

View file

@ -0,0 +1,306 @@
# Homework 6: Search as a Programming Technique
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** 11:59pm Monday, April 17
## Introduction
You will solve two searching problems in this assignment. The first
is a tautology checker, the second is a maze runner.
## Part 1: Tautology Checker
### Getting Started
Your task for this homework is to write tautology checker for logical
formulas. You should be familiar with the code we wrote for the
``subsetsum`` problem in lecture before starting this assignment. *If
you don't understand that code, then the work below will be very
difficult.*
Below are a few type definitions and functions that you will want to
use. As we've done before, place these in a file named ``tautology.ml``
in a directory named ``Hwk_06``. This ``Hwk_06`` directory should be
in your individual repository.
Our formulas are represented by the following OCaml type:
```
type formula = And of formula * formula
| Or of formula * formula
| Not of formula
| Prop of string
| True
| False
```
You will implement the tautology checker in the style that uses
exceptions, specifically the style that raises an exception to
indicate that the search process should continue.
Thus we will use the following exception for that.
```
exception KeepLooking
```
Our tautology checker will search for substitutions for the
propositions in the formula for which the formula will evaluate to
``false``. The substitutions are similar in structure to the
environments that we had in homework 4.
We'll introduce a name for them, ``subst``, with the following type
definition.
```
type subst = (string * bool) list
```
Here is a function for converting substitutions to
strings. We'll use this in printing our results.
```
let show_list show l =
let rec sl l =
match l with
| [] -> ""
| [x] -> show x
| x::xs -> show x ^ "; " ^ sl xs
in "[ " ^ sl l ^ " ]"
let show_string_bool_pair (s,b) =
"(\"" ^ s ^ "\"," ^ (if b then "true" else "false") ^ ")"
let show_subst = show_list show_string_bool_pair
```
You might also find these helpful
```
let is_elem v l =
List.fold_right (fun x in_rest -> if x = v then true else in_rest) l false
let rec explode = function
| "" -> []
| s -> String.get s 0 :: explode (String.sub s 1 ((String.length s) - 1))
let dedup lst =
let f elem to_keep =
if is_elem elem to_keep then to_keep else elem::to_keep
in List.fold_right f lst []
```
### Formula evaluation
In the process of searching for substitutions for a formula that
indicate that the formula is not a tautology, we will want to evaluate
a formula for a given substitution.
This is very much like the expression evaluators that we created for
homework 4, except now the environment (or substitution as we've
called it here) is a part of the input of the evaluation process.
This is because our formulas have variables, here called propositions,
but they don't have let-clauses that would introduce binding into an
environment. So that complete environment/substitution must be passed
in.
Write a function named ``eval`` with the type
``formula -> subst -> bool`` that evaluates the input formula using
the input substitution to return a value of ``true`` or ``false``.
In case a proposition occurs in the formula that does not appear in
the substitution, just raise an exception. The tests run on your code
will only provide proper substitutions (no missing binding) to
evaluations.
For example:
```
eval (And ( Prop "P", Prop "Q")) [("P",true); ("Q",false)]
```
will evaluate to ``false``
and
```
eval (And ( Prop "P", Prop "Q")) [("P",true); ("Q",true)]
```
will evaluate to ``true``.
### "Freevars" for formulas
The search space for our tautology checker is the set of substitutions
for the propositions in a formula. Thus we must determine what
variables are used in a formula.
This is reminiscent of the ``freevars`` function we wrote for
expressions in homework 4.
Write a function ``freevars`` with the type ``formula -> string list``
that returns the names of all the propositions in the formula. Be
sure to remove duplicates since when we create substitutions based on this
list we cannot have more than one binding for the same propositional
variable.
Here are a few evaluations.
```
freevars (And ( Prop "P", Prop "Q"))
```
evaluates to ``["P"; "Q"]`` and
```
freevars (And ( Prop "Q", Prop "P"))
```
evaluates to ``["Q"; "P"]``. But order doesn't matter.
Here are a few examples of possible tests that may be run on your
solution:
```
List.exists ( (=) "P" ) (freevars (And ( Prop "P", Prop "Q"))) = true
List.length (freevars (And ( Prop "P", Or (Prop "Q", Prop "P")))) = 2
```
### Tautology Checker
Given the types and functions defined above we can now get to writing
our tautology checker.
The function will be named ``is_tautology`` and will have the type
``formula -> (subst -> subst option) -> subst option``.
The first argument is, not surprisingly, the formula we wish to check.
The second argument is a function similar to the ``process_solution``
functions we used in the implementations of ``subsetsum``. This
function is called when the search process finds a substitution for
which the formula evaluates to ``false``. The function gets this
substitution as input and can decide to accept it (perhaps by
interacting with the user) and in that case returns that substitution
in a ``Some`` value.
If the function does not accept the solution, it should raise the
``KeepLooking`` exception.
We could then use ``is_tautology`` in some interesting ways, just
like we did for ``subsetsum`` in lecture. First,
we could define the following to return the first substitution that is
found:
```
let is_tautology_first f = is_tautology f (fun s -> Some s)
```
Second, we could define the following to print out all substitutions
and always raise the ``KeepLooking`` exception. It will then print
all substitutions that make the formula evaluate to ``false`` and the
finally return ``None`` as the value of the call to
``is_tautology_print_all``.
```
let is_tautology_print_all f =
is_tautology
f
(fun s -> print_endline (show_subst s);
raise KeepLooking)
```
For the formula
```
Or (Prop "P", Not (Prop "P"))
```
your tautology checker should find that it is a tautology and never
generate a substitution.
For the formula
```
Or (Prop "P", Prop "Q")
```
your tautology checker should find that it is not a tautology and
generate (among others) the substitution
```
[ ("Q",false); ("P",false) ]
```
or
```
[ ("P",false); ("Q",false) ]
```
since the order in which binding appear in the substitution does not
matter.
For the formula
```
And ( Prop "P", Prop "Q")
```
your tautology checker should find that it is not a tautology and
generate 3 substitutions (if they are all demanded) that make the
formula false.
Try
```
is_tautology_print_all (And (Prop "P", Prop "Q"))
```
and make sure you see the proper 3 substitutions.
Your functions will also be checked that they conform to the proper
style and use exceptions as described in class to implement
backtracking search.
## Part 2: Maze Runner
Consider the maze shown below, where "S" marks the start position and
"G" marks the two goal positions.
<img src="http://www-users.cs.umn.edu/~evw/Maze.png" alt="Maze" width ="300px"/>
Your task is to write a function ``maze`` that returns the first
path from an "S" position to one of the "G" positions that is found.
To keep this computation from happening when we load the file into
utop, the function should take ``()`` the unit value as input and
return a list of pairs on integer representing a path, wrapped up in
an ``option`` type.
This function will have some things in common with the
wolf-goat-cabbage problem that we solved in class, so make sure you
understand that before attempting this one.
It is suggested that you write a function ``maze_moves`` that takes a
position as input with the type ``int * int`` and then returns a list
of positions, with type ``(int * int) list`` that are all the
positions that could be moved to, while respecting the boundaries and
lines in the maze. For example, one cannot move from position
``(3,1)`` to position ``(4,1)``.
You can implement this with a rather large ``match`` expression that
just hard-codes the moves seen in the image above.
For example, your solution may execute the search in an order so that
``maze ()`` evaluates to
```
Some [ (2,3); (1,3); (1,2); (2,2); (3,2); (3,3); (3,4); (4,4); (4,5); (3,5) ]
```
Or, your ``maze`` function might instead return the path
```
Some [ (2,3); (1,3); (1,2); (2,2); (3,2); (3,3); (4,3); (5,3); (5,2); (5,1) ]
```
Thus, the type of ``maze`` must be
```
unit -> (int * int) list option
```
Your maze runner solution should use the same ``KeepLooking``
exception to indicate when a deadend in the search has been reached or
when some other reason exists to keep looking for solutions.
Your functions will also be checked that they conform to the proper
style and use exceptions as described in class to implement
backtracking search.

View file

@ -0,0 +1,262 @@
# Homework 7: Using OCaml Modules
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** 26th April 2017, 5:00 PM
## Introduction
In class and in Chapter 9 of Real World OCaml we developed various
modules for implementing intervals.
Of particular interest were the means for constructing signatures (the
``module type`` declarations), defining modules that implemented a
signature (the ``module`` declarations that name a signature) and
functors.
In this assignment you have some freedom in how you organize your code
in different files. But you need at least one file, ``hwk_07.ml``,
that is placed in a ``Hwk_07`` directory
in your individual repository.
We will use the command
```
ocamlbuild hwk_07.byte
```
to compile your program. Since ``ocamlbuild`` will detect dependencies
and compile the named programs you can put some modules in different
files.
Since ``ocamlbuild`` is not installed on the CSE labs machines you can't test
this. Use ``utop`` instead and ensure that some combination of ``#mod_use`` and ``#use``
directives can be used to load your code.
**Before you start** read these specifications twice. Your first
reading will give you an overview of what is required but may leave
you with some questions since there are some dependencies between the
various components that make it difficult to enumerate all the
requirements in a linear fashion. On a second reading you will find
answers to these questions.
## Functors and Signatures for Vectors
We have seen a few examples for implementing intervals and will use
what we learned from those to design modules and signatures for
operations over vectors. When the size of a vector is 3, it has a
simple geometric interpretation in 3 dimensional space, but we will
consider vectors of any size.
Our vectors will be specified by a signature defining vector
operations (to be described below) and this signature should keep the
type for representing vectors abstract; that is, it is hidden so that
users of the vector cannot manipulate the underlying representation
directly.
Furthermore, vectors are to be parameterized by the kind of numeric
module that supports some basic arithmetic operations. This module
describes the type and its supporting operations that are the elements
of a vector. We could have a vector of integers, a vector of floating
point numbers, a vector of complex numbers, even a vector of intervals
of integers. These "arithmetic" modules must implement the same
signature, perhaps called ``Arithmetic_intf`` (but this choice of name
is up to you). This signature is similar to the ``Interval_intf`` we
defined in lecture.
Thus, you will need to define a functor for creating vectors. This
functor takes a module as input that will support the operations that
let it be treated as a number. This is similar to the ``Make_interval``
functor in the interval examples.
This input module should implement an interface that specifies what
operations (functions) are needed to treat it as a number so that we
can use it as the type of values in a vector. Presumably there will
need to be an operation for adding and multiplying these values. We
may also need values identifying the identity for addition (that is,
what the value zero is for this type) and the identity for
multiplication (that is, what the value one is for this type). These
are only suggestions as the operations in this signature are really
determined by what is required by the operations in the vector
functor. This signature is similar to the ``Comparable`` signature
used in the interval examples. That ``Comparable`` signature had to
provide operations that were used in the functions in the
``Make_interval`` functor.
The operations that must be supplied by the module created by the
functor are described below.
+ ``create``. This function take in an integer specifying the size of
the vector and an initial value to be copied into all positions in the
new vector. The type of this initial value is determined by the
module given as input to the functor ``Make_vector``.
+ ``from_list``. This function takes a list of element values and
returns a vector containing those values.
+ ``to_list``. This function takes a vector and returns a list
containing the values in the vector.
+ ``scalar_add``. This function takes a value and an vector in which
the value is the same type as the elements of the vector. It returns
a new vector whose values are computed by adding this value to each
element of the input vector.
+ ``scalar_mul`` This function takes a value and an vector in which
the value is the same type as the elements of the vector. It returns
a new vector whose values are computed by multiplying this value with each
element of the input vector.
+ ``scalar_prod``. This function take two vectors of the same type
and computes their scalar product, sometimes called the dot product.
This value is returned in an ``option`` type.
This operation requires that the vectors have the same number of
elements. It then multiplies each corresponding pair of values from
the two vectors and then adds the up. This multiply and addition
operation are the ones defined in the module for the vector element
type (that is, the input to the ``Make_vector`` functor).
If the vectors are not the same size, the value of ``None`` is
returned. If they are the same size, then it returns ``Some `` *x*
where *x* is the scalar product of the two vectors.
+ ``to_string`` converts a vector to a string. This string wraps a
vector in double angle brackets and prints its size and values. The
size and values are separated by a vertical bar ``|`` and the values
are separated by commas. Examples can be seen below.
+ ``size``. This function takes a vector and returns the number of
elements contained in it.
To summarize, the following are needed:
1. A signature for the vector modules
2. A functor for creating vector modules that implements the signature
for vector modules.
3. A signature defining the type and operations for the numeric values
that will be the elements of the vector. Modules implementing this
signature will be the input module to the functor.
## Modules and Signatures for Vector Elements and Vectors
We will create two modules that will be given to the ``Make_vector``
functor; these are to be named ``Int_arithmetic`` and
``Complex_arithmetic``. The first is for integer values, the second
for complex numbers in which the real and imaginary parts are floating
point numbers.
These are similar in spirit to the ``Int_comparable`` modules in
``v6/intInterval.ml``.
Just as ``Int_comparable`` exposed the representation type (there it
was ``int``) here the ``Int_arithmetic`` should also expose this
``int`` type and the ``Complex_arithmetic`` module should expose the
representation type for complex numbers - specifically ``float *
float``. This can be seen in the examples below.
Both of these will implement the signature for numeric values (the
signature in #3 in the list above).
Thus, we can define the following two modules for two different kinds
of vectors:
```
module Int_vector = Make_vector (Int_arithmetic)
module Complex_vector = Make_vector (Complex_arithmetic)
```
Thus you are required to define the following names of the objects
described above:
+ ``Make_vector``
+ ``Int_arithmetic``
+ ``Complex_arithmetic``
+ ``Int_vector``
+ ``Complex_vector``
## Example evaluations
Assuming the above declaration of ``Int_vector``
has been made consider the following declarations
of integer vectors.
```
let v1 = Int_vector.create 10 1
let v2 = Int_vector.from_list [1;2;3;4;5]
let v3 = Int_vector.scalar_add 3 v2
let v4 = Int_vector.scalar_mul 10 v2
let i1 = Int_vector.scalar_prod v3 v4
let l1 = Int_vector.to_list v3
let i2 = Int_vector.size v4
let s1 = Int_vector.to_string v1
let s2 = Int_vector.to_string v2
let s3 = Int_vector.to_string v3
let s4 = Int_vector.to_string v4
```
When these declarations are loaded in to utop (assuming the
appropriate module declarations have already been loaded) the
following is displayed by utop when it reports what has been loaded:
```
val v1 : Int_vector.t = <abstr>
val v2 : Int_vector.t = <abstr>
val v3 : Int_vector.t = <abstr>
val v4 : Int_vector.t = <abstr>
val i1 : int option = Some 1000
val l1 : int list = [4; 5; 6; 7; 8]
val i2 : int = 5
val s1 : string = "<< 10 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 >>"
val s2 : string = "<< 5 | 1, 2, 3, 4, 5 >>"
val s3 : string = "<< 5 | 4, 5, 6, 7, 8 >>"
val s4 : string = "<< 5 | 10, 20, 30, 40, 50 >>"
```
As you can see, the four vectors ``v1``, ``v2``, ``v3``, and ``v4``
have abstract values that cannot be displayed by utop.
Also, we can see how the vectors are converted to strings.
This should make it clear how the various vector functions operate.
Similar behavior is observed when complex numbers are used in the
vector instead of integers.
When ``Complex_vector`` has been defined as shown above, consider the
following declarations:
```
let v5 = Complex_vector.from_list [ (1.0, 2.0); (3.0, 4.0); (5.0, 6.0) ]
let v6 = Complex_vector.scalar_add (5.0, 5.0) v5
let c1 = Complex_vector.scalar_prod v5 v6
let s5 = Complex_vector.to_string v5
let s6 = Complex_vector.to_string v6
```
When loaded into utop, the following is displayed by utop when it
reports what has been loaded:
```
val v5 : Complex_vector.t = <abstr>
val v6 : Complex_vector.t = <abstr>
val c1 : Complex_vector.elemType option = Some (-36., 193.)
val s5 : string = "<< 3 | (1.+2.i), (3.+4.i), (5.+6.i) >>"
val s6 : string = "<< 3 | (6.+7.i), (8.+9.i), (10.+11.i) >>"
```
(Note that in the type of ``c1`` you see the name that the solution
uses for the element type, namely ``elemType``. You may choose another
name for this. Assessments will not be based on this particular
name.)

View file

@ -0,0 +1,44 @@
# Homeworks
#### Homework 1, due January 30 at 5:00pm
Intoduction to OCaml.
Graded out of 100 points.
#### Homework 2, due February 17 at 5:00pm
Working with higher order function
Graded out of 71 points.
#### Homework 3, due March 1 at noon.
Inductive proofs of programs
Graded out of 70 points.
#### Homework 4, due March 22 at 5:00pm
Programs as data.
Graded out of 100 points.
#### Homework 5
Lazy evaluation
Not yet graded.
#### Homework 6
Search
No yet out.
#### Homework 7
Modules
Not yet out.

View file

@ -0,0 +1,65 @@
# Setup Git to use SSH
By now you are probably sick of typing in your username and password every time
you need to pull or push to the Git repo. You can avoid this by using SSH to
connect to GitHub. With HTTPS, you must authenticate with a username and
password every time you want to connect to your repo on GitHub. With SSH, you
authenticate via file stored on your computer.
*Note: Please use your internet id (i.e. smith082) in place of
"YOUR_INTERNET_ID" throughout this tutorial*
### Creating an SSH identity
To use SSH to connect to GitHub, you must first create an SSH identity (key).
This is done with the `ssh-keygen` command.
```shell
$ ssh-keygen -t rsa -b 4096 -C "YOUR_INTERNET_ID@umn.edu"
```
The following will show up in your terminal. Press enter when you are asked
where to save your SSH key. This will save it in the default location.
```
Generating public/private rsa key pair.
Enter file in which to save the key (/home/YOUR_INTERNET_ID/.ssh/id_rsa):
Created directory '/home/YOUR_INTERNET_ID/.ssh'.
```
When you are prompted to enter a passphrase, press enter again.
```
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
```
You should now have two keys saved in `/home/YOUR_INTERNET_ID/.ssh`.
`id_rsa` - This is your **private** key and should be kept secret. Do not share
this with anyone or put this on the internet.
`id_rsa.pub` - This is your public key. It is safe to share this, and it can be
used to identify you uniquely when logging in.
### Configuring GitHub to recognize your SSH identity
Go to https://github.umn.edu/settings/keys and click on *New SSH key*.
You should now be prompted to enter in a Title and Key. The title can be
whatever you want (e.g. "main"). The Key should be your SSH **public** key. Your
public key can be found by with the command below:
```shell
$ cat ~/.ssh/id_rsa.pub
```
Copy the output from this command into the Key section and click *Add SSH key*.
### Changing Git remote
To switch over from HTTPS to SSL, cd into your private repo and run this
command.
```shell
$ git remote set-url origin git@github.umn.edu:umn-csci-2041S17/repo-YOUR_INTERNET_ID.git
```

View file

@ -0,0 +1,75 @@
# How to use OCaml
### Install OCaml
Go back to your account home directory:
```
% cd
```
Execute the following commands
```
% module add soft/ocaml
% module initadd soft/ocaml
```
The first makes the OCaml tools available for your current shell
session, the second makes them available for future shell sessions.
Execute the following:
```
% which ocaml
```
If it does not display the path to the ocaml compiler
(it should be **/soft/ocaml-4.03.0/linux_x86_64/bin/ocaml**)
then talk to your TA.
# PLEASE FIX BELOW ! ! !
### Use OCaml
Go back to the lab_01 directory in your individual repository.
Perhaps by the following commands:
```
% cd
% cd csci2041/repo-user0123/Lab_01
```
Start the OCaml interpreter:
```
% ocaml
```
At the Ocaml prompt (#) enter the following (do type "#use", not just
"use"):
```
# #use "fib.ml" ;;
```
Note that the prompt is "#" and directives to the interpreter to load
files and quit also begin with a "#".
OCaml will report an error in the program:
> File "fib.ml", line 10, characters 30-31:
> Error: Unbound value n
Quit OCaml using the "quit" command as illustrated below:
```
# #quit ;;
```
Use an editor of your choice (emacs, vim, gedit, etc.) to replace
the variable `n` with the correct variable `x`.
Also, replace the text between dots in the comment with your name if
you've not already done so.
Save the file and repeat the steps above to start OCaml and load the file.
Now compute the 5th Fibonacci number by typing the following:
```
# fib 5 ;;
```
There is a bug in this program. It will return `16` instead of the
correct answer of `5`. Let's fix that bug now.

View file

@ -0,0 +1,18 @@
# HowTo ...
The intention for this directory is to collect various "how-to"
documents that are needed during the semester.
Some of these may be contributed by you, the students in this course.
Examples include documents on how to use OCaml interpreters, how to
install OCaml on different operating systems, how to install OCaml
editing modes in different editors. It is hoped that this information
can be carried forward to future instances of this course.
+ `How-to-use-OCaml.md` describes how one used OCaml on CSE labs
machines.
+ `Git_SSH_Setup.md` describes how to setup your SSH key to
authenticate with github.

View file

@ -0,0 +1,514 @@
# Lab 1: Getting started with GitHub and OCaml
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, January 20 at 5:00pm. You should be able to complete
lab work during the lab. But occasionally some work may not get
completed, thus this due date.
# Introduction
### Goals of this lab:
+ In this lab you will set up your University of Minnesota GitHub
repository that will be used to turn in your homework assignments for
this course.
+ You will also install the OCaml compilers and associated tools that we
will be using in this class.
+ Finally, you will modify an OCaml program, run it, and turn it in
via GitHub.
+ There should be enough computers for each person to have their own.
+ If you have a laptop, please feel free to use it for this if you want. But keep in mind that your homework is graded on the lab machines and must work there.
### GitHub:
* The University of Minnesota has its own GitHub installation that we
will be using in the course. We **are not** using
https://github.com.
* Git is a software version control system that we will be using in
the class. You will turn your work in using GitHub, not Moodle. We
will provide feedback on your work using GitHub as well.
### Set up your CSE Labs account if you do not have one
If you do not yet have a CSE Labs account (and thus your partner had
to log into the computer) perhaps because your are a College of
Liberal Arts student, then create that account now.
To do so, go to this web site and fill in the requested information:
https://wwws.cs.umn.edu/account-management/
# Lab Steps to Complete
## Working with GitHub and Git
### Initialize your GitHub account
+ If you've never logged into https://github.umn.edu, then do so now.
Then give your University Internet Id (we'll call this UIID for short)
to a lab TA so that they can set up your repository. This is the same
as the part of your University email address that comes before the
`@umn.edu` part of the address.
+ Note that this is **not** your student ID number that appears on
your student Id card. We will never ask you for that number.
+ If you've already logged into https://github.umn.edu, then proceed
to the next step since your repository should already be set up.
### Verify that your 2041 repository is set up
+ If your University Internet Id is **user0123** then your repository
will be named **repo-user0123**. In the examples and text below,
replace **user0123** with your University Internet Id.
+ Log into https://github.umn.edu and select the **umn-csci-2041S17**
organization and click on the repository named **repo-user0123**.
+ At the URL
https://github.umn.edu/umn-csci-2041S17/repo-user0123
you should see a list of files in your repository. This will
include only a file named **README.md**.
If this file is not there, speak to a TA in your lab.
This repository is a database containing the files and the history of
all their changes made since they were added to the repository. It is
much more than a simple copy of a set of files.
### Setting up Git in your CSE Labs account
+ Log into your CSE (College of Science and Engineering) account.
+ Verify Git is installed. Execute the following:
(But don't type the `%`. That is meant to symbolize the prompt you see in your terminal window. It may be different that the `%` sign. You'll just type `git --version`. Don't type the `%` in any of the other examples below.)
```
% git --version
```
+ Configure Git.
You need to tell Git what your name, email address and favorite
editor are. Below is the series of commands that you should
enter. Be sure to fill in the appropriate details for yourself
```
% git config --global user.name "YOUR NAME HERE"
% git config --global user.email "YOUR UMN EMAIL ADDRESS"
% git config --global core.editor "nano"
```
If you have a favorite editor (for example `emacs` or `vim`) then you can enter the command to start that editor where you entered `nano` above.
Note that your name appears between double quotes since it has spaces
in it. Your email address doesn't, so it doesn't need to be in
quotes. If you would like "emacs -nw" as your editor (emacs such that
it doesn't open a new window but opens in the terminal) then you'll
want double quotes around that 2 word phrase.
Check that these are set correctly; execute
```
git config -l
```
### Create a space for your Git workspaces
Create a directory in your CSE account named "csci2041"
(You can use some other name, of course, but in the discussion we will assume that you used "csci2041").
```
% mkdir csci2041
% cd csci2041
```
In **csci2041** we will put copies of a "public" read-only repository
containing files that we want to distribute to you during the semester
and also space for your individual repository that only you and the
TAs and instructor have access to.
### Clone your individual repository
The Git "clone" operation makes a copy of a repository and places it
in your account. This copy contains the files and also all the
history of changes---just like the repository stored on
https://github.umn.edu.
In your **csci2041** directory, execute the following
```
% git clone http://github.umn.edu/umn-csci-2041S17/repo-user1234.git
```
After entering your UIID credentials this will create the directory
called repo-user1234.
It will contain a **README.md** file.
Execute the following:
```
% cd repo-user1234
% ls
```
When you clone your repository Git will create some hidden files
stored in the **.git** directory that contain the long name of this
repository, so that we won't need to type it anymore.
This directory contains the copy of the repository with all the past
history of changes to the files and other information. So now there
are two copies of your repository.
These hidden files tell Git where the GitHub central server is so that
operations involving the server won't need this long name.
Execute the following:
```
% ls -a
% ls .git/
```
Modifying these hidden **.git** files by hand, or creating them by
copying directories, is an extremely bad idea. It will cause you many
headaches with Git. **So don't do it!**
*You only need to do this clone step once to initially install the
repository in your account.*
If you have another computer and want to do some of your homework on
that, then you will need to repeat this step for that computer as
well.
### Commands that access the repository
The following command reads these hidden files and will tell you the
URL of the central repository, and some other information.
Execute the following:
```
% git remote --verbose
```
A status operation will also tell you if you've made changes to your
workspace since the last time you updated it with files from the
repository. This is important because we grade your work by getting it
out of your repository. If it is in your workspace but not the central
GitHub repository we can't see it and it won't be graded.
Run the following: ``` % git status ``` Since the files in your
workspace (see below) and repositories (both local and the one on
https://github.umn.edu) are the same, Git tells you as much.
### Working files
So, if the hidden directory **.git** is another copy of the
repository, what are the files in this directory?
These files are copies of the files that you can edit. You can create
new files and delete files that are no longer needed. **But**, we
will need to "commit" any changes that we make to these files to the
repository, eventually, so that the repository and the "working files"
in your account are synchronized.
Create a `Lab_01` directory and change into it by executing the following:
```
% mkdir Lab_01
% cd Lab_01
```
Using the text editor of your choice, create a file named `fib.ml`
in your just-created `Lab_01` directory. Copy the following OCaml
code into that file and save it.
```
(* Author: Eric Van Wyk
Modified by: ... replace the text between the dots with your name ... *)
(* A function computing the Fibonacci sequence: 1, 1, 2, 3, 5, 8, ... *)
(* There is a bug in the following program. Can you fix it? *)
let rec fib x =
if x < 3 then 1 else fib (n-1) + fib (n-1)
```
Add your name into the comment on the second line of the file.
Don't worry about the rest of the file, we will learn how to read this
OCaml code soon enough.
### Adding files
Check the status of your working files and repository by executing the
following:
```
% git status
```
This tells you that there is now an "untracked" file named `fib.ml`
and that Git is not tracking changes to this file. To tell Git to do
so, we must `add` the file using the following command:
```
% git add fib.ml
```
Now run `git status` again and see what it says. What is Git telling
you here?
### Committing changes
Git is now aware of this file and sees that changes have been made
that have not be "committed". Only "committed" changes to the file
will be pushed up to the central GitHub server (http://github.umn.edu)
and thus it is only these that will be graded or assessed.
To commit the file changes you've made, execute the following
```
% git commit -a -m "Adding my name to the file"
```
Now go back to your browser and refresh the page showing your
repository. Does this file show up there?
No, it doesn't. The **commit** command adds your changes to your
local repository only. We now need to **push** those changes from
your local repository up to the one stored on https://github.umn.edu.
We will do that next.
But first, run ``` % git status ``` What is it telling you? Your
changes are committed to the local repository but not the "central"
one on https://github.umn.edu
### Pushing changes
Type
```
% git push
```
This pushes your changes from your local repository up to the central
one.
Run
```
% git status
```
again. It should now tell you that your working copy of the files and
both repositories are all synchronized.
In your web browser, check that a file named `Lab_01_Feedback.md` has
been added to your repository. You can click on the link to see its
contents. These files will typically be generated for your
assignments as soon as you push changes to your programs up to GitHub.
If the results here are not what you expect then you need to either
fix the issues identified with your program, or, talk to a TA or post
a question on Moodle to see if there is a problem with the tools that
automatically generate these files.
In this case, these tools will verify that your program is in the
right directory, in the right file, but broken and not yet working.
Later in the lab you will fix the problems.
### Clone the public repository
Go back to your **csci2041** directory, by executing the following
command:
```
% cd ../..
```
Now clone the public class repository by executing the following
command:
```
% git clone http://github.umn.edu/umn-csci-2041S17/public-class-repo.git
```
In the directory `Labs` you will see the Markdown file
`Lab_01_Jan_17.md` from which this web page is generated.
When we add new files to the central repository you will be asked to
execute the following:
```
% git pull
```
This "pulls" changes from the central repository down to your local
one and updates the working copy of those files in your account with
the changes.
Try it. It doesn't have any effect, but it doesn't cause any harm
either.
## Working with OCaml
### Install OCaml
Go back to your account home directory:
```
% cd
```
Execute the following commands
```
% module add soft/ocaml
% module initadd soft/ocaml
```
The first makes the OCaml tools available for your current shell
session, the second makes them available for future shell sessions.
Execute the following:
```
% which ocaml
```
If it does not display the path to the ocaml compiler
(it should be **/soft/ocaml-4.03.0/linux_x86_64/bin/ocaml**)
then talk to your TA.
### Use OCaml
Go back to the `Lab_01` directory in your individual repository.
Perhaps by the following commands:
```
% cd
% cd csci2041/repo-user0123/Lab_01
```
Start the OCaml interpreter:
```
% ocaml
```
At the Ocaml prompt (#) enter the following (do type "#use", not just
"use"):
```
# #use "fib.ml" ;;
```
Note that the prompt is "#" and directives to the interpreter to load
files and quit also begin with a "#".
OCaml will report an error in the program:
> File "fib.ml", line 10, characters 30-31:
> Error: Unbound value n
Quit OCaml using the "quit" command as illustrated below:
```
# #quit ;;
```
Use an editor of your choice (emacs, vim, gedit, etc.) to replace
the variable `n` with the correct variable `x`.
Also, replace the text between dots in the comment with your name if
you've not already done so.
Save the file and repeat the steps above to start OCaml and load the file.
Now compute the 5th Fibonacci number by typing the following:
```
# fib 5 ;;
```
### Fix the sample file
Using a text editor edit the `fib.ml` file.
Fix the bug in the definition of the function `fib`. You should just
need to replace a `1` with a `2` somewhere and replace the variable
`n` with the variable `x`.
There is a bug in this program. It will return `16` instead of the
correct answer of `5`. Let's fix that bug now.
Also, `fib 0` will return `1` instead of `0`. You will need an additional
if-then-else expression to check if `x = 0` and then evaluate to `0`. The 'else' branch of this new
if-then-else expression will be the if-then-else expression that is there now. It should look something like
the following
```
let rec fib x =
if x = 0 then 0 else
if x < 3 then 1 else fib (n-1) + fib(n-2)
```
After you've saved the file, test it. Fire up `ocaml` again and see
if you get the right answer.
### Commit and push so that the corrected version is up on GitHub.
Now you need to just turn in your work. First, see what Git says
about the status of your files
```
% git status
```
It tells you that a file has been modified.
You now need to commit your changes and push them up to your central
GitHub repository.
Execute the appropriate `git add` and `git commit` commands and the
run `git push`. What is the error message that you get?
It is telling you that changes have been made to code up on the GitHub
server and that you need to "pull" those changes before you can "push"
new changes up to the repository.
Execute the following:
```
% git pull
```
The above command "should" work and seems to work for many of you. If it doesn't you'll get a message about the need to "merge" the local repository in your account with the repository up on the https://github.umn.edu server.
You will be put into an editor window where you could enter a message to document this "merge" operation. There is no need for this comment, so just save the file. Now try `git pull` again. It should tell you that your repository is already up to date.
You should now see that feedback file in your local directory (just
above your `Lab_01` directory. Verify that it is there.
Now do a `git push` to see that your changed code is pushed up to the
Github server. Also verify that the feedback file gets updated to
indicate that your code is now working properly.
Verify that this worked, by using your browser to see the changes on
https://github.umn.edu.
This concludes Lab 01.
### **Due:** Friday, January 20 at 5:00pm.
You should be able to complete lab work during the lab. But
occasionally some work may not get completed, thus this due date.
Note that these changes must exist in your repository on
https://github.umn.edu. Doing the work, but failing to push those
changes to your central repository cannot be assessed.
After the due date, we will typically run additional tests and the
results of this assessment will be put a file named
`Lab_01_Assessment.md` in your repository. This file will have your
score for the lab. A similar pattern will be repeated for other
assignments.

View file

@ -0,0 +1,176 @@
# Lab 2: Introduction to OCaml
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, January 27 at 5:00pm. You should be able to complete
lab work during the lab. But occasionally some work may not get
completed, thus this due date.
# Introduction
### Goals of this lab:
+ In this lab you will write a few functions in OCaml to begin the
process of learning how best to use it.
+ You will create a file named *lab_02.ml* in a *Lab_02* directory
inside your individual class repository.
Since we use some scripts in grading different aspects of your work
it is critical that you name your directories and files exactly as
specified.
# Getting started.
In your individual repository, make a directory named *Lab_02* and
change into it:
```
% mkdir Lab_02
% cd Lab_02
```
Create an empty file with the name *lab_02.ml*:
```
% touch lab_02.ml
```
It is in this file that you will put your solutions to the programming
problems below.
In a separate terminal window, run ``utop`` (the system used in lecture) or ``ocaml``
to start the OCaml interpreter. To load the contents of your file type
```
#use "lab_02.ml" ;;
```
at the ``#`` prompt. As you edit your file you'll need to do this again each time
to load your changes.
# OCaml programming
Some of the functions that you are asked to solve below are similar to
ones we've solved in class. These can be found in ``simple.ml`` in
the ``code-examples`` directory of the public repository. Open
another tab in your browser and you can see that file
[here](https://github.umn.edu/umn-csci-2041S17/public-class-repo/blob/master/SamplePrograms/simple.ml).
### #1 Circle area, version 1
Write a function named ``circle_area_v1`` with the type ``float ->
float``. This function should take as input the **diameter** (not the
radius) of a circle and compute its area.
For this version, do not use any nested let-expressions or function
calls; only use literals like ``3.1415`` and floating point operators
such as ``*.``, ``+.``, or ``/.``.
For example, ``circle_area_v1 2.5`` should evaluate to about ``4.90``.
### #2 Circle area, version 2
Now write another version of this function, this time named
``circle_area_v2`` with the same type as ``circle_area_v1``.
This version, however, must use a nested let-expression to define the
constant ``pi`` to have the appropriate value. If there are any
computations that are duplicated (perhaps computing the radius from
the diameter provided as input) use a nested let-expression to give
that sub-computation a name and use it accordingly in the computation.
### #3 Product of a list of elements
In lecture, we wrote a function to compute the sum of all the values
in a list of integers. It had the type ``int list -> int``.
Write a similar function named ``product`` that computes the product
of the values in a list of integers. It will have the same type as
the sum function.
For example, ``product [2; 3; 4]`` should evaluate to ``24``.
### #4 Sum of differences
For this problem, you are to write a function that again has the type
``int list -> int``. It must be called ``sum_diffs`` and it will
compute the sum of the differences between all successive pairs of
numbers in the list.
For example, ``sum_diffs [4; 5; 2]`` will evaluate to ``2``.
You just write a recursive list-processing function for this task,
despite the fact that some arithmetic simplification of the
computation (in the case above it would be (4-5) + (5-2)) would let us
do the computation in just one subtraction operation. Write the function so
that it carries out the operation naively and computes the difference between
each successive pair of numbers.
You may assume that this function will only be passed lists of length
2 or more, so you don't need patterns to handle, for example, the
empty list. Instead, our "base case" will be a pattern that matches
a 2-element list, like the following:
```
| x1::(x2::[]) -> x1 - x2
```
This pattern has something more complex than a simple name to the
right of the ``::`` cons constructor. It has another nested pattern that
matches a list of one element. Together, this pattern only
matches lists with exactly 2 elements. Note that the parenthesis are
not required here; they only make it explicit that the cons
constructor is right associative.
You will also need and another pattern that matches lists of 2 or more
elements. This second pattern will need to bind 2 elements of the
list some names so that your expression can compute their difference.
It will be similar to the one above, but you need to figure out the
details.
Don't hesitate to discuss this problem with your fellow students or your TAs.
### #5 2D points and distance between them
Tuples in OCaml are simple data structures for holding a few values of
possibly different types. For example, we might represent points on a
plane using two floating point values in a tuple. This type is
written ``float * float``.
A function that returns ``true`` if a point is in the "upper-right" quadrant
of a plane might be implemented as follows:
```
let upper_right (x,y) = x > 0.0 && y > 0.0
```
This function has the type ``float * float -> bool``.
Implement a function named ``distance`` with type ``float * float ->
float * float -> float`` to compute the distance between two points
(each represented as a tuple of 2 ``float`` values).
You may find the ``sqrt`` function useful in this function.
### #6 Triangle perimeter
This problem asks you to compute the perimeter of a triangle. For a
triangle with 3 corners named p1, p2, and p3, the perimeter is the
distance from p1 to p2 plus the distance from p2 to p3 plus the
distance from p3 back to p1.
Implement a function named ``triangle_perimeter`` with type ``float *
float -> float * float -> float * float -> float`` to do this.
*This concludes lab 02.*
**Due:** Friday, January 27 at 5:00pm. You should be able to
complete lab work during the lab. But occasionally some work may not
get completed, thus this due date.
Note that these changes must exist in your repository on
github.umn.edu. Doing the work, but failing to push those changes to
your central repository cannot be assessed.

View file

@ -0,0 +1,224 @@
# 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.

View file

@ -0,0 +1,12 @@
# Lab 4: Higher order functional programming
*CSci 2041: Advanced Programming Principles, Spring 2017*
For this lab, you will begin working on Homework 2.
You can find its specifications on the public class repository in the
``Homework`` directory.
Use this time to read the specifications over carefully and completely
and ask your TAs any qusetions that may arise.

View file

@ -0,0 +1,65 @@
# Lab 5: Quiz 1 results, OCaml versions, Hwk 2
*CSci 2041: Advanced Programming Principles, Spring 2017*
This lab has three components.
1. return quiz 1, discuss results, ask questions
2. use the locally installed version of OCaml
3. run initial tests
4. remove warnings from your ``hwk_02.ml`` file
## Quiz 1
Your TAs will return your quiz and go over solutions to the problems.
We will not dedicate lecture time to the quiz, so be sure to get your
questions answered during lab.
## OCaml versions.
To make sure we are using the same version of OCaml that you are using
for your work, please ensure that you are using version 4.02.3.
(This check is to be done on your CSE labs account. If you are using
your own laptop for your work, make sure you tests on your CSE labs
account or verify that the automated feedback is what you expect.)
Type the following to check the version number:
```
ocaml -version
```
If you get something else, try
```
module initrm soft/ocaml
```
and then try the previous version check.
If you still are using something other than version 4.02.3 then ask
your TA to help you fix the problem.
### The automated tests
The automated feedback tests for homework 2 are now turned on. Make a
change to your solution and push it to trigger the tests.
Make sure that the tests are what you expect and that either your
solution is passing them all or you understand why it is not.
### No warnings
In homework 2 the automated tests code that has warnings is not
graded. You do need to remove all warning from your code.
In doing this, you may need to raise exceptions for data that is not
expected by one of your functions. Follow the guidelines we used in
lab 3 for doing this.

View file

@ -0,0 +1,200 @@
#Lab 6: Inductive Data Types
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, the 24th of February, at 5.00PM.
This lab will focus on tree based inductive data types and on functions surrounding these.
##PART A:
For this part of the lab, use the following type for ``tree``:
```
type 'a tree = Leaf of 'a
| Fork of 'a * 'a tree * 'a tree
```
Some examples using this:
+ ``let t1 = Leaf 5``
+ ``let t2 = Fork (3, Leaf 3, Fork (2, t1, t1))``
+ ``let t3 = Fork ("Hello", Leaf "World", Leaf "!")``
Below are a list of functions that you will need to implement on this:
#### 1. ``t_size``
Given an 'a tree, write a function ``t_size`` to find the size of the tree. This function will be of the type: ``'a tree -> int``.
Example:
``t_size t3`` gives ``int = 3``.
#### 2. ``t_sum``
Given an int tree, write a function ``t_sum`` to find the sum of the values in the tree. The type of the function is: ``int tree -> int``.
Example:
```
let t4 = Fork (7, Fork (5, Leaf 1, Leaf 2), Fork (6, Leaf 3, Leaf 4))
```
``t_sum t4`` gives ``int = 28``.
#### 3. ``t_charcount``
Write a function ``t_charcount`` that takes a string tree as input and count the total number of characters that the values contain. Type of the function is: ``string tree -> int``.
Example:
```
t_charcount t3 gives int = 11.
```
#### 4. ``t_concat``
Now, write a function ``t_concat`` that will concatenate together the values of a string tree. Type of this function is: ``string tree -> string``.
Example:
```
t_concat t3 gives string = "HelloWorld!".
```
##PART B:
In this part, you will introduce options as well into the above tree type and handle those cases.
An example:
```
let t5 : string option tree = Fork (Some "a", Leaf (Some "b"), Fork (Some "c", Leaf None, Leaf (Some "d"))).
```
This is a tree of type ``string option tree``.
Write 4 new functions, similar to the ones above, but that work over trees with ``option`` values in them
For example, ``t_opt_size`` should count the number of values in the tree that is under the "Some" constructor.
The types are as follows:
1) ``t_opt_size : 'a option tree -> int``
Example:
```
let t7 = (Fork (Some 1, Leaf (Some 2), Fork (Some 3, Leaf None, Leaf None)))
t_opt_size t7
```
gives
```
int = 3
```
2) ``t_opt_sum : int option tree -> int``
Example:
``t_opt_sum t7`` gives ``int = 6``.
3) ``t_opt_charcount : string option tree -> int``
Example:
```
let t8 = (Fork (Some "a", Leaf (Some "b"), Fork (Some "c", Leaf None, Leaf (Some "d"))))
t_opt_charcount t8
```
gives
```
int = 4
```
4) ``t_opt_concat : string option tree -> string``
Example:
```
t_opt_concat t8
```
gives
```
string = "abcd"
```
##PART C:
In class, function ``tfold`` with type ``('a -> 'b) -> ('a -> 'b -> 'b -> 'b) -> 'a tree -> 'b`` was introduced. Here is the function:
```
let rec tfold (l:'a -> 'b) (f:'a -> 'b -> 'b -> 'b) (t:'a tree) : 'b =
match t with
| Leaf v -> l v
| Fork (v, t1, t2) -> f v (tfold l f t1) (tfold l f t2)
```
The next set of questions will require you to write all the questions in PART A and PART B using tfold and without using recursionss.
Name and the type are as follows:
```
1) tf_size : 'a tree -> int
2) tf_sum : int tree -> int
3) tf_char_count : string tree -> int
4) tf_concat : string tree -> string
5) tf_opt_size : 'a option tree -> int
6) tf_opt_sum : int option tree -> int
7) tf_opt_char_count : string option tree -> int
8) tf_opt_concat : string option tree -> string
```
##PART D:
This is the final part of this lab. Instead of using the above tree, will we now use a tree of this type:
```
type 'a btree = Empty
| Node of 'a btree * 'a * 'a btree
```
This is a sorted tree, and will enable us to create empty trees as well as trees with size two.
Using this tree type, do the following:
#### ``bt_insert_by``
Write a function ``bt_insert_by`` that will take a comparator, an element and a btree as the input, and insert this element into the btree using the comparator to find the correct position. As the tree is sorted, the criteria to insert is as follows:
1. the values in the left subtree is always less than or equal to the value of the Node.
2. the values in the right subtree is always greater that the value of the Node.
The following is the type:
``bt_insert_by : ('a -> 'a -> int) -> 'a -> 'a btree -> 'a btree``
Example:
```
let t6 = Node (Node (Empty, 3, Empty), 4, Node (Empty, 5, Empty))
bt_insert_by compare 6 t6
```
returns
```
int btree = Node (Node (Empty, 3, Empty), 4, Node (Empty, 5, Node (Empty, 6, Empty)))
```
You can use the comparator found in the Pervasives (Pervasives.compare) module, but, ensure that you understand how it actually works, lest it inserts the element in the incorrect location.
#### ``bt_elem_by``
Like in the homework, write a similar function ``bt_elem_by`` that will now take as inputs a function, an element and a btree, and return true if the element exists in the tree, or, false otherwise. The type of the function is:
``bt_elem_by : ('a -> 'b -> bool) -> 'b -> 'a btree -> bool``
Example:
```
let t6 = Node (Node (Empty, 3, Empty), 4, Node (Empty, 5, Empty))
bt_elem_by (=) 4 t6
```
returns ``true``
#### ``bt_to_list``
Create a function ``bt_to_list`` that will take as a btree and create a list of all the values in the btree. The type of the function is ``'a btree -> 'a list``.
Example: ``bt_to_list t6`` will return
``int list = [3; 4; 5]``.
#### ``btfold``
Like ``tfold``, now create a function ``btfold`` of the type ``'b -> ('b -> 'a -> 'b -> 'b) -> 'a btree -> 'b``.
#### ``btf_elem_by``
Write ``btf_elem_by`` so that it has the same behaviour as ``bt_elem_by`` that you wrote above but this one must not be
recursive and must instead use ``btfold``.
#### ``btf_to_list``
Similarly, write a new function named ``btf_to_list`` that has the same behaviour as your ``bt_to_list`` function that you wrote above. But now use ``btfold`` instead without recursion. Call this function ``btf_to_list``.
#### ``btf_insert_by`` ? ?
Finally, write a comment on why will using ``btfold`` for ``bt_insert_by`` might be difficult.

View file

@ -0,0 +1,35 @@
#Lab 7: Reasoning About Correctness
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** This work is due before you leave lab today.
In lab today your TAs will present you with some sample OCaml
functions and also a property that you will be asked to prove a
property of the functions.
You will be given about 10 minutes to work on this problem before the
solution is discussed and explained.
Create a directory called ``Lab_07`` and in the directory create a
file named ``lab_07.txt`` that is a text file the contains both the
sample functions given to your lab and also the proof of the property
that you are asked to prove.
Your text file must contain well-marked answers to the following
questions:
1. What is the induction principle for the type of data used in the
sample functions?
2. What is the property that you are asked to prove?
3. What is the "base case" that you need to prove?
4. What is the "inductive case" that you are asked to prove?
5. What is the "inductive hypothesis" that you can use in proving the
inductive case?
These are questions that you may be asked to answer on the quiz on
Wednesday.
Commit and push this file before you leave lab today.

View file

@ -0,0 +1,193 @@
Lab 8: Improving your OCaml Code
================================
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, March 10 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
Similar to what you did in Lab 03,
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 02 and allow them to view your solution.
This sort of sharing is only allowed, obviously, after the due date.
You will also:
0. Format your code for readability and understanding (see below) .
1. Confirm there are no `;;` in your ocaml file. They are only are used when
interacting with the interpreter
2. Remove all clumsy list construction (see Lab 03).
3. Remove all warnings from the file (see Lab 03).
## Prepare the files for Lab 8
Create a new directory named `Lab_08` and copy your `hwk_02.ml` file from
`Hwk_02` into this new directory. Keep the filename as `hwk_02.ml`.
You will all want to log into the computer on your desk. Then you should log
into GitHub and open the page with `hwk_02.ml` so that your solution is visible.
## Code Formatting
In this part, you're not going to make any functional improvements to your
programs, instead you will be enhancing your code's readability and to try to
help your future partners (and TAs) better understand the logic behind your
code.
Code formatting is an important part of writing software. While most languages
don't require any special formatting for the function of your software,
maintaining a consistent and readable code style can make
understanding and reasoning about your software much easier for yourself and
your peers. This is true for OCaml and any other language you find yourself
working in.
We highly recommend that you attempt to emulate the style that is recommended
by the OCaml community.
[The OCaml website has a style guide that can be found here.]
(https://ocaml.org/learn/tutorials/guidelines.html) Nearly any questions about
style and formatting for OCaml can be answered by this guide. We'd like to see
everyone try to use these styles on future assignments.
The guide has a key point that it makes right at the beginning:
>**Writing programs law:** A program is written once, modified ten times, and
>read 100 times. So simplify its writing, always keep future modifications in
>mind, and never jeopardize readability.
This statement is true in any language. Speaking from experience, as the size of
the application you work on increases, this statement gains more and more truth.
For the purpose of this assignment, we'd like you to make a few modifications to
your code to try to improve your partners' ability to read and understand your
logic. Here are a couple of guidelines for common constructs that can be found
within most OCaml files (reiterated from the above guide):
`if cond then e1 else e2` statements should be written differently based on the
size of `cond`, `e1`, and `e2`. If they are short, then writing them on the same
line is fine. As the size of the statements grow, place them on their own lines.
Here is an example where both `e1` and `e2` are long:
```OCaml
if cond then
e1
else
e2
```
`match` statements should have the clauses aligned with the beginning of the
construct. If the expression to the right of the pattern matching arrrow is
too large, cut the line after the arrow.
```OCaml
match lam with
| Abs (x, body) ->
1 + size_lambda body
| App (lam1, lam2) ->
size_lambda lam1 + size_lambda lam2
| Var v -> v
```
**Naming lambda expressions** is something you have encountered in this
assignment. When the body of lambda expression is long (spanning a few lines
give the function a name by defining it in a let-expression. Consider this
lambda expression:
```OCaml
List.map
(fun x ->
blabla
blabla
blabla)
l
```
Rewrite it as a let statement and name the function like this:
```OCaml
let f x =
blabla
blabla
blabla in
List.map f l
```
This is much clearer to read, in particular if the name given to the function is
meaningful.
Please make these style changes to the `Lab_08/hwk_02.ml` file, then commit
and push them to your repository before continuing. You can also continue to
improve your code as your work with your group. Please try to style your code
like this on future assignments :)
## Form Groups
Find 1 or 2 people that you want to work with in lab. Feel free to move around
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_02.ml` so that your solution is visible.
Please make sure that your formatting changes have been committed and pushed!
As you start to work on your assignment, make sure to include a comment with
the names of your group members! It is important to give credit for your
improvements!
## Determine the Questions to Share
As a group, figure out which 3 functions you want to share with each other. You
will be sharing, discussing, and improving your implementations.
Do not choose the either of the two simplest functions from the assignment
`length` and `rev`. Treat `is_elem` and `is_elem_by` as one function for the
purposes of improvement. Make sure to include at least one of `paradelle` or
`convert_to_non_blank_lines_of_word` as one of the functions you discuss.
## Discuss the First Function
In this part, you will add a new comment above the function 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_02.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, make sure that the `;;` are removed from the file, there are no
clumsy list construction instances, and that there are no warnings raised.
Review the Lab 03 description for details on these issues.
Compare your usages of the functions you wrote in Part 1 (`dedup`, `split_by`,
etc) in Part 2 and 3. Did you effectively use these functions in your
definitions for `convert_to_non_blank_lines_of_word` and `paradelle`? Did you
rewrite some of the functionality of `is_elem` inside of your `split_by`
implementation? Discuss with your group members when and where you used your
useful functions from Part 1.
## Repeat this Process for the Next 2 Functions
If you run low on time and can only complete two functions that is fine.
## What to Turn In
You need to turn in your new `hwk_02.ml` file in the new `Lab_08` 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. Also, try to format them for
readability.

View file

@ -0,0 +1,14 @@
# Lab 9: Expression evaluation
*CSci 2041: Advanced Programming Principles, Spring 2017*
This lab time is dedicated to solving the lingering issues you
may have with your Hwk 04 assignment.
Use this time to discuss any questions you have with the TAs. Also
make sure that your code is passing the feedback tests that you expect
it to pass - inspect the ``Hwk_04_Feedback.md`` file that is pushed to
your repository after any updates and push that you do.
If you've completed Hwk 4 you need not attend this lab.

View file

@ -0,0 +1,133 @@
# Lab 10: Lazy Evaluation
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, March 31 at 5:00pm. You should be able to complete
this work during lab.
## Lab goals
Doing this lab should get you set up to work on the programming
portion of Homework 05 as some of the programming tasks in this lab
are similar to those in the homework.
## Getting started.
Copy the file ``streams.ml`` from the public class repository into a
new directory named ``Lab_10``. Name the copy of this file
``lab_10.ml``.
Add the following functions to the end of that file. But you should
clearly mark the parts of this file that you did not write and
attribute them to their author (your instructor) and then indicate
where your work starts in the file by adding you name and a comment to
this effect.
Read over the contents of this file and test out the various functions
by running them so that you understand how these streams work.
## Streams
Below you are asked to define a series of values (some are functional
values) that lead to the definition of ``str_105_nats`` of type
``string`` so that it prints out as seen in the following interaction
in utop:
```
utop # print_endline str_105_nats ;;
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
11, 12, 13, 14, 15, 16, 17, 18, 19, 20
21, 22, 23, 24, 25, 26, 27, 28, 29, 30
31, 32, 33, 34, 35, 36, 37, 38, 39, 40
41, 42, 43, 44, 45, 46, 47, 48, 49, 50
51, 52, 53, 54, 55, 56, 57, 58, 59, 60
61, 62, 63, 64, 65, 66, 67, 68, 69, 70
71, 72, 73, 74, 75, 76, 77, 78, 79, 80
81, 82, 83, 84, 85, 86, 87, 88, 89, 90
91, 92, 93, 94, 95, 96, 97, 98, 99, 100
101, 102, 103, 104, 105
- : unit = ()
```
#### natural numbers as strings
First define a function ``str_from`` of type ``int -> string
stream``. This function has the same behavior as ``from`` defined in
``streams.ml`` except that the numbers are converted into strings.
For example, ``take 5 (str_from 10)`` should evaluate to the string
list ``["10"; "11"; "12"; "13"; "14"]``.
Next, define ``str_nats`` of type ``string stream``. It corresponds
to ``nats``.
The expression ``take 5 str_nats`` should evaluat to the string list
``["1"; "2"; "3"; "4"; "5"]``.
#### a stream of separator strings
In the output above, you'll notice that some numbers are separated by
a comma and a space and that others are separated by a newline. You
now need to define
the stream of strings that are these appropriate separator values.
Write a function ``separators`` with the type
``int -> string -> string stream``.
Let's call the first argument ``n`` and the second one ``sep``.
This function returns a stream of strings in which the first ``n``
elements of the stream are the string ``sep``, and the next one is
that string containing a single new line character, that is ("\n").
Then there are ``n`` more copies of ``sep`` before another string with
the newline character. This pattern repeats indefinitely.
For example, the expression
``take 10 (separators 2 "$$")`` will evaluate to the following string
list
```
["$$"; "$$"; "\n"; "$$"; "$$"; "\n"; "$$"; "$$"; "\n"; "$$"]
```
Keep in mind that the second input to ``take`` is a steam, not a
list. Only its output is a list.
When you look at the result of the test for this function in your
Feedback file on GitHub please note that the newline characters do not
appear in the sample output that your expression is supposed to match.
That output displays the values and the embedded newline characters
just create new lines. But Markdown does not treat newlines as
paragraph brakes so the strings just appear mashed together in the
output. Just make sure your output of this function looks correct.
Next define the value ``ten_per_line`` of type ``string stream`` that
can be used in the following functions to help create the output of
``str_105_nats`` seen above.
#### alternate
Next, write the function ``alternate`` that has the type
```
'a stream -> 'a stream -> 'a stream
```
and combines two streams into one by alternating their values. The
result of evaluating the expression ``take 10 (alternate nats (from
100))`` is the int list
```
[1; 100; 2; 101; 3; 102; 4; 103; 5; 104]
```
#### the string with 105 natural numbers
Finally, define ``str_105_nats`` that as type ``string`` and whose
value can be seen above.
To this you'll need to combine the functions and values you've defined
above, perhaps some from ``streams.ml``, and also either
``List.fold_left`` or ``List.fold_right`` to fold up some list of
strings (that originally came from ``str_nats`` and out of a call to
``separators``) into the single string seen above.
Note that the newline characters don't appear in the output in the
Feedback file on GitHub for the test for ``str_105_nats`` either.
Just make sure that your output looks like the example above
and that there are now extra spaces except as part of the
"comma and space" separator value passed into your function
``separators``.

View file

@ -0,0 +1,155 @@
# Lab 11: Denotational Semantics
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, April 7 at 5:00pm. You should be able to complete
this work during lab.
## Lab goals
This lab will explore the interpreter that we wrote in lecture and
extend it a bit.
## Getting started.
Copy the file ``interpreter.ml`` from either the
``SamplePrograms/Sec_01_1:25pm`` or
``SamplePrograms/Sec_10_3:35pm`` directory of
the public class repository into a
new directory named ``Lab_11``. Name the copy of this file
``interpreter.ml``.
## Review
Open the file and familiarize your self with the data types and the
functions ``eval`` and ``exec``.
Run ``exec program_2 []``. And enter ``10``. How many times does
``sum`` appear in the final state?
Create a let-binding in your file of the from
```
let num_sum = ...
```
where ``...`` is replaced by the number of times that ``sum`` appears
in the final state.
## Add and conditional statement -- if-then-else
Add a new constructor of the following form to ``stmt``:
```
| IfThenElse expr * stmt * stmt
```
Then write to clause for this new constructor in the ``match``
expression in ``exec``.
Next, add a modulus operator constructor to ``expr``:
```
| Mod of expr * expr
```
Then write the clause for this new constructor in the ``match``
expression in ``eval``.
Hint: in OCaml, ``mod`` is the infix operator for modulus. Try it out
in ``utop``.
## Another program:
Construct a new program of type ``stmt`` named ``program_3``. It
should correspond to the following comment already in
``interpreter.ml``. Note how ``program_1`` and ``program_2`` both
have similar comments for them. You need to construct a let-binding
for ``program_3`` that corresponds to the program in the comment below:
```
(* read x;
i = 0;
sum_evens = 0;
sum_odds = 0;
while (i < x) {
write i;
if i mod 2 = 0 then
sum_evens = sum_evens + i;
else
sum_odds = sum_odds + i;
i = i + 1
}
write sum_evens;
write sum_odds
*)
```
## Test your extended ``exec``
Run ``exec program_3 []`` and enter ``8`` when prompted to enter a
number.
It should print out the integers from 0 to 7 and the print 12 and then
16.
Run ``exec program_3 []`` and enter ``15`` when prompted.
Create the following let-bindings in your file:
```
let val_sum_evens =
let val_sum_odds =
let num_sum_evens =
let num_sum_odds =
```
+ give ``val_sum_evens`` the value of ``sum_evens`` in the final state
+ give ``val_sum_odds`` the value of ``sum_odds`` in the final state
+ give ``num_sum_evens`` the number of times ``sum_evens`` appears in
the final state
+ give ``num_sum_odds`` the number of times ``sum_odds`` appears in
the final state
## Create a testable version of program_3
Define ``program_3_test`` in your file to be the same as
``program_3``, but replace
```
Read "x"
```
with
```
Assign ("x", Value (Int 12))
```
The following should evaluate to ``Int 30``
```
lookup "sum_evens" (exec program_3_test [])
```
This is one of the automated tests.
## An if-then and a skip statement
Add the following constructors to ``stmt``:
```
| IfThen of expr * stmt
| Skip
```
The first is the if-then statement you should expect. The second is a
"skip" statement that does nothing. It is like ``pass`` in Python or
a "noop" in assembly language.
Complete the implementation of ``exec`` to handle these new
constructs. You might implement the ``IfThen`` construct based on
the observation that executing "if ...cond... then ...stmt..." is the same as
executing "if ...cond... then ...stmt... else skip".
Next, define ``program_4`` to correspond to to the following comment:
```
(* y = 0;
if x mod 2 = 0 then y = y + 2;
if x mod 3 = 0 then y = y + 3;
if x mod 4 = 0 then y = y + 4;
*)
```
Now try ``exec program_4 [ ("x",Int 4) ]``.
For example ``lookup "y" (exec program_4 [ ("x",Int 4) ])`` should
evaluate to ``Int 6``.
## Push your work.
Now be sure to commit and push your work. Check the feedback file
that should be generated each time you push this work.

View file

@ -0,0 +1,13 @@
# Lab 12: Search as a Programming Technique
*CSci 2041: Advanced Programming Principles, Spring 2017*
In this lab you will begin working on Hwk_06 - the assignment on Search.
You will find the specifications for Hwk_06 in the usual place. You
should try to complete at least ``eval`` and ``freevars`` during lab.
You will also be able to collect your second attempt at Problem 3 on
Quiz 3.

View file

@ -0,0 +1,15 @@
# Lab 13: Hwk 5 review and starting Hwk 7
*CSci 2041: Advanced Programming Principles, Spring 2017*
In this lab your TAs will present solutions to problems from Hwk 5
that you want to see. This will be done on the whiteboards in
lecture.
If you have questions about the assessment of your Hwk 5, then ask the
TA that graded your work. They should be in your lab. If not, ask
another TA, they should also be able to help you.
In this lab you may also begin working on Hwk_07 - the assignment on
modular programming using OCaml modules. You will find the
specifications for Hwk_07 in the usual place.

View file

@ -0,0 +1,10 @@
# Lab 14: Hwk 7 and Review of Quiz 4 Solutions
*CSci 2041: Advanced Programming Principles, Spring 2017*
In this lab your TAs will present solutions to problems on Quiz 4
that you can use in understanding your work on that quiz. Please
ask questions about these solutions and how the problems
were assessed.
Afterwards, please use the TAs as a resource for working on Homework 7.

Binary file not shown.

View file

@ -0,0 +1,240 @@
# Lab 15: Parallel Evaluation
*CSci 2041: Advanced Programming Principles, Spring 2017*
**Due:** Friday, Mary 5 at 5:00pm. You may be able to complete
this work during lab, but it may take a bit more time.
## Lab goals.
In this lab you will get a chance to see how to generate parallel code
based on the map and fold constructs we've studied in class.
This lab was developed by Nathan Ringo, who has been working in my (Eric's) lab
and doing some interesting things with parallel programming in Cilk. Thanks Nathan!
## Getting started.
Copy the file ``Lab_15.tar`` from the ``Labs`` directory in the public
class repository into your individual repository. This file should be
at the top level - next to all the feedback and assessment files.
Next, type
```
tar xf Lab_15.tar
```
and this should create a ``Lab_15`` directory in which you will do
your work.
Next, type
```
git add Lab_15
```
Now Git knows about all the files you'll need to turn in. You'll need
to commit and push these later, of course.
## Exploring the code.
You'll notice that there are a lot of files in this directory.
It is a working compiler for a small functional language. It includes
specifications for a scanner and parser. It includes a definition of
the source and target language as well as how to do some of the
translation.
#### Examples
To get a feel for the source language, look at the ``*.src.txt`` files
in the ``examples`` directory.
There you'll find a few programs in the source language. These
consist of a sequence of functions (which may be no functions) and a
final expression that is evaluated (often calling these functions).
You don't need to write any more example programs.
#### Run the translator.
Try running some examples. Return to the ``Lab_15`` directory and
type
```
./build.sh examples/arithmetic.src.txt
```
You should see OCaml build the translator, display and compile this
program and show the result - which should be 7.
Try it also for ``funcs.src.txt`` and ``mapseq.src.txt``.
Also, try ``fibseq.src.txt``. Note how long this takes? When you
implement parallel map this parallel version in ``fib.src.txt`` should
execute faster.
The parallel map in ``map.src.txt`` and ``fib.src.txt`` and the fold
in ``fold.src.txt`` are not yet implemented. So don't run these yet.
#### The source language
Next, navigate to the ``src`` directory and look at ``common.ml``.
This file has definition of binary operators that is shared by both
the source and target language. It also contains some functions for
accessing the environment - but you shouldn't need to use these.
Next, look in the ``src_lang.ml`` file. This file defines the
``Src_lang`` module (as a file-based module).
Here you see the definition the program syntax, starting with
``program`` - indicating that a source language program is a list of
functions (``func``) and an expression (``expr``).
Functions (``func``) consist of a name, then a list of parameters
(each parameter has a name and a type) and then the expression that is
the body of the function.
Expressions and types then follow. These should be mostly self
explanatory, but few comments are worthwhile.
+ ``Array`` creates an array from a list of values
+ ``Call`` is a function call. Note that functions are just names, as strings.
+ ``Fold`` takes just a function name and an expression that should be
an array.
+ ``Map`` will map the function (again just a name) across the array
expression.
+ ``MapSeq`` is a sequential version of map. ``Map`` will have to be
translated into parallel code.
The remainder of the file does some type checking of the code. You
need not read this.
#### The target language
Now take a look at ``tgt_lang.ml`` which defines the target language
of this translator. Programs written in the source language (as
defined in ``src_lang.md``) are translated into the target language
(as defined in ``tgt_lang.md``).
The target language is similar to the source in that these programs
are also a collection of functions followed by an expression.
(The target language is then translated to C and executed. This was
visible when running the ``build.sh`` script.)
Expressions are similar but there are some interesting additions
+ ``ArrayGet`` will access an array (the first ``expr``) at some index
(the second ``expr``). Indexing begins at position 0, like in C.
+ ``ArraySize`` returns the size of an array.
+ ``Block`` lets statements be written as expressions. A ``Block``
expression contains a list of statements that are executed. These are
followed by an expression. This expression is then evaluated and the
value of this expression is returned as the value of the ``Block``
expression.
+ ``Call`` function calls are the same as before
+ ``Spawn`` will spawn the evaluation of an expression. But the
expression that a ``Spawn`` contains should be a ``Call`` expression.
Statements are also part of this target language.
+ ``ArraySet`` is an assignment-like statement that sets the value of
an array (the first ``expr``) at an index position (the second
``expr``) to be a new value (the third ``expr``).
+ ``Decl`` creates a new declaration of a variable using it name,
type, and initializing value.
+ A ``For`` loop declares a new integer index variable (its first
``string`` argument) that ranges from the value of the second ``expr``
up to 1 LESS THAN the third ``expr``. This lets one write for loops
that range over all indices of an array by counting from 0 to the size
of the array. The body of the for loop is ``stmt list``.
+ ``Sync`` is the Cilk sync statement we discussed in class.
+ ``Sleep`` will delay execution and might be useful in writing slow
functions that will exhibit speedup when run in parallel. Without
this, some functions are so fast that we don't notice any parallel
speedup.
#### The translation
OK, you should now look at ``translate.ml``. This defines the
functions ``translate``, ``translate_expr``, ``translate_func``
and a few more.
##### Some simple cases
You need only worry about ``translate_expr`` and the incomplete
definition of ``fold_helper``.
First look over ``translate_expr``. It does some type checking of the
source language program and translates it to the target language.
The first case of the ``match`` translate array expressions. It does
this by mapping the translate function over the list of expressions
(``arr``) that are the value that go into the array.
Similarly, translations of ``BinOp`` binary operations like ``Add``
and ``Mul`` are simple too.
Translating Boolean values (``Bool``) and function calls are also
simple.
There are other simple translations later in the file.
##### Sequential map.
The first interesting translation is for the sequential version map
function. This code does some type checking and then at the end
generates the ``Tgt_lang.Block`` construct that is the translation of
``MapSeq``. This block declares the output array, followed by the for
loop that sequentially walks down the array applying the function over
array elements. The final expression is just a reference (``Var
"_map_array"``) that is the value returned.
##### Parallel map.
The first task you should complete it to implement the parallel
version of the map. This is the ``Map`` construct.
It is suggested that you copy the translation for the sequential map
and modify it by adding appropriate ``Spawn`` and ``Sync`` constructs.
When finished, return to the ``Lab_15`` directory and run
```
./build.sh examples/map.src.txt
```
to run the parallel version of ``mapseq.src.txt``
Note that there is no noticeable speedup since the work to be done is so
small.
But you should see a difference between ``figseq.src.txt`` and the
parallel ``fib.src.txt``.
##### The fold construct
The next interesting translation is for ``Fold`` operations. Here
the provided code does some type checking and translates to a function
call to the ``fold_helper`` function that is defined (partially) at
the top of this file. This is what we discussed in class last week.
The ``fold_helper`` function should implement the parallel execution
of a fold function.
There is nothing to do here. Instead you should complete the
definition of ``fold_helper``.
This will require some careful consideration. So think about this
before you start programming. Ask yourself
+ where will the result be stored?
+ since there is no assignment statement, how can I use ``ArraySet``
to store a single value? Hint: create an array of size 1 and store a
temporary result in it.
To get started, an array of size 1 named ``out`` is already
declared.
+ what do the parameter ``start`` and ``end`` represent, exactly?
what values of ``start`` and ``end`` indicate that the range
contains only 1 item?
This part of the lab will be considered as extra credit worth about
1/2 of what a lab is worth.

View file

@ -0,0 +1,81 @@
# Labs
#### Lab 1, Jan 17
Getting started with OCaml and Git.
Graded out of 100 points.
#### Lab 2, Jan 24
Introduction to OCaml.
Graded out of 65 points.
#### Lab 3, Jan 31
Improving your Hwk 01 OCaml code.
Graded out of 62 points.
#### Lab 4, Feb 7
Higher order functional programming, getting started on Hwk 02.
Not graded.
#### Lab 5, Feb 14
Discussion of Quiz 1 results.
Not graded.
#### Lab 6, Feb 21
Inductive data types.
Graded out of 73 points.
#### Lab 7, Feb 28
Reasoning about correctness.
Not graded.
#### Lab 8, Mar 7
Improving your Hwk 02 OCaml code.
Will be graded, not yet completed.
#### Lab 9, Mar 21
Working on expression evaluation, Hwk 4
Not graded.
#### Lab 10, Mar 28
Working with streams in OCaml.
Will be graded, not yet completed.
#### Lab 11, Apr 4
Denotational semantics.
Will be graded, not yet completed.
#### Lab 12, Apr 11
Search
Will be graded, not yet completed.
#### Lab 13, Apr 18
#### Lab 14, Apr 25
#### Lab 15, May 2

View file

@ -0,0 +1,108 @@
let rec sum = function
| [] -> 0
| x:xs -> x + sum xs
P(l1, l2): sum (l1 @ l2) = sum l1 + sum l2
P(l1) : \forall l2 in 'a list .
sum (l1 @ l2) = sum l1 + sum l2
Base case: l1 = []
----------
P([]) :
\forall l2 in 'a list . (
sum ([] @ l2)
= sum l2, by properties of lists and append
= 0 + sum l2, by properties of aritmetic
= sum [] + sum l2, by def of sum
)
Inductive case: l1 = h::t
---------------
show: P(h::t) : \forall l2 in 'a list .
sum ((h::t) @ l2) = sum (h::t) + sum l2
given: P(t) : \forall l2 in 'a list .
sum (t @ l2) = sum t + sum l2
\forall l2 in 'a list . (
sum ((h::t) @ l2)
= sum ( h :: (t @ l2 ) ), by properties of lists and append
= h + sum ( t @ l2 ), by def of sum
= h + sum t + sum l2, by inductive hypothesis
= sum (h::t) + sum l2, by def of sum
)
----------------------------------------------------------------------
let rec reverse l = match l with
| [ ] -> [ ]
| x::xs -> reverse xs @ [x]
P(l1, l2): reverse (l1 @ l2) = reverse l2 @ reverse l1
Induction over l1.
Base case: l1 = []
----------
reverse ([] @ l2)
= reverse l2, by properties of append and lists
= reverse l2 @ [], by properites of empty lists
= reverse l2 @ reverse [], by def. of reverse
Inductive case: l1 = h::t
-----------
given: reverse (t @ l2) = reverse l2 @ reverse t
reverse (h::t @ l2)
= reverse (h :: (t @ l2)), by properties of lists and append
= reverse (t @ l2) @ [h], by def. of reverse
= reverse l2 @ rerverse t @ [ h ], by ind. hypo.
= reverse l2 @ reverse (h::t), by def. of reverse
------------------------------------------------------------
P(e, l): is_elem e (place e l)
Induction over l
Base case: l = []
is_elem e (place e [])
= is_elem e [e], by def of place
= (e = e || e > e && is_elem e []), by def of is_elem
= true, by properties of equality.
Inductive case: l = h::t
show: is_elem e (place e (h::t))
given: is_elem e (place e t)
... exercise for the reader ....

View file

@ -0,0 +1,59 @@
double x = x + x
double (fact 10)
-- eagerly- call by value
double (fact 10)
double 3628800
3628800 + 3628800
7257600
-- call-by-name
double (fact 10)
(fact 10) + (fact 10)
3628800 + (fact 10)
3628800 + 3628800
7257600
-- call-by-need , lazy evaluation
double (fact 10)
x + x where x = fact 10
x + x where x = 3628800
3628800 + 3628800
7257600
take 2 (makefrom 4 15)
Use the following definitions (clearly not OCaml syntax):
take n [] = []
take 0 (x::xs) = []
take n (x::xs) = x::take (n-1) xs
makefrom 0 v = []
makefrom n v = v :: makefrom (n-1) (v+1)
-- call by name --
take 2 (makefrom 4 15)
= take 2 (15::makefrom (4-1) (15+1))
= 15::take (2-1) (makefrom (4-1) (15+1))
= 15::take (2-1) (makefrom 3 (15+1))
= 15::take (2-1) ((15+1)::makefrom(3-1) ((15+1)+1))
= 15::take 1 ((15+1)::makefrom(3-1) ((15+1)+1))
= 15::(15+1)::take (1-1) (makefrom(3-1) ((15+1)+1))
-- call by value --
take 2 (makefrom 4 15)
= take 2 (15::makefrom (4-1) (15+1))
= take 2 (15::makefrom 3 16)
= take 2 (15::16::makefrom 2 17)
= take 2 (15::16::17::makefrom 1 18)
= take 2 (15::!6::17::18::makefrom 0 19)
= take 2 (15::16::17::18::[])
= 15::take 1 (16::17::18::[])
= 15::16::(take 0 17::18::[])
= 15::16::[]
-- don't skip steps, things like 3+1 need to be evaluated properly

View file

@ -0,0 +1,7 @@
Exercise #3:
Write a function named cond that has the same behavior as OCamls
if-then-else construct.
let cond c t f = if c then t else f

View file

@ -0,0 +1,114 @@
let rec sum = function
| [] -> 0
| x:xs -> x + sum xs
P(l1, l2): sum (l1 @ l2) = sum l1 + sum l2
P(l1) : \forall l2 in int list . sum (l1 @ l2) = sum l1 + sum l2
Base case : l1 = []
--------------------
\forall l2 in int list . (
sum ([] @ l2)
= sum l2, properties of append and lists
= 0 + sum l2, properties of addition
= sum [] + sum l2, by def of sum
)
Inductive case : l1 = h::t
--------------------
show: \forall l2 in int list . sum ((h::t) @ l2) = sum (h::t) + sum l2
given: \forall l2 in int list . sum (t @ l2) = sum t + sum l2
\forall l2 in int list .
sum ((h::t) @ l2)
= sum ( h :: (t @ l2) ), properties of lists and append
= h + sum ( t @ l2 ), by def. of sum
= h + sum t + sum l2, by inductive hypothesis
= sum (h::t) + sum l2, by def. sum
)
------------------------------------------------------------
let rec reverse l = match l with
| [ ] -> [ ]
| x::xs -> reverse xs @ [x]
P(l1, l2): reverse (l1 @ l2) = reverse l2 @ reverse l1
Induction over l1.
Base case:
----------
P([], l2): reverse (l1 @ l2) = reverse l2 @ reverse l1
reverse ([] @ l2)
= reverse l2, by properties of append and lists
= reverse l2 @ [], by properties of append and lists
= reverse l2 @ reverse [], by def. of reverse
Inductive case
--------------
P( (h::t, l2):
show: reverse ((h::t) @ l2) = reverse l2 @ reverse (h::t)
given: reverse (t @ l2) = reverse l2 @ reverse t
reverse ((h::t) @ l2)
= reverse ( h :: ( t @ l2 ) ), by properties of append and lists
= reverse ( t @ l2 ) @ [h], by def. of reverse
= reverse l2 @ reverse t @ [h], by inductive hypothesis
= reverse l2 @ (reverse t @ [h]), by associtativity of append
= reverse l2 @ reverse (h::t), by def. of reverese
--------------------------------------------------
let rec place e l = match l with
| [ ] -> [e]
| x::xs -> if e < x then e::x::xs
else x :: (place e xs)
let rec is_elem e l = match l with
| [ ] -> false
| x::xs -> e = x || (e > x && is_elem e xs)
P(e, l) : is_elem e (place e l)
Induction over l.
Base:
is_elem e (place e [])
= is_elem e [e], by def. of place
= e = e || (e > e && is_elem e []), by def is_elem
= true, because e = e
Inductive case:
... exercise for the reader ...

View file

@ -0,0 +1,79 @@
double x = x + x
double (fact 10)
-- eagerly- call by value
double (fact 10)
double 3628800
3628800 + 3628800
7257600
-- call-by-name
double (fact 10)
(fact 10) + (fact 10)
3628800 + (fact 10)
3628800 + 3628800
7257600
-- call-by-need , lazy evaluation
double (fact 10)
x + x where x = fact 10
x + x where x = 3628800
3628800 + 3628800
7257600
-- An exercise from class --
take 2 (makefrom 4 5)
Use the following definitions (clearly not OCaml syntax):
take n [] = []
take 0 (x::xs) = []
take n (x::xs) = x::take (n-1) xs
makefrom 0 v = []
makefrom n v = v :: makefrom (n-1) (v+1)
-- call-by-need
take 2 (makefrom 4 5)
= take 2 (5 :: makefrom (4-1) (5+1))
= 5 :: take (2-1) (makefrom (4-1) (5+1))
= 5 :: take (2-1) (makefrom 3 (5+1))
= 5 :: take (2-1) (v :: makefrom (3-1) (v+1)) where v = 5+1
= 5 :: take 1 (v :: makefrom (3-1) (v+1)) where v = 5+1
= 5 :: v :: take (1-1) (makefrom (3-1) (v+1)) where v = 5+1
= 5 :: v :: take (1-1) (makefrom (3-1) (v+1)) where v = 6
= 5 :: 6 :: take (1-1) (makefrom (3-1) (6+1))
= 5 :: 6 :: take (1-1) (makefrom 2 (6+1))
= 5 :: 6 :: take (1-1) (v :: makefrom (2-1) (v+1)) where v = 6+1
= 5 :: 6 :: take 0 (v :: makefrom (2-1) (v+1)) where v = 6+1
= 5 :: 6 :: []
-- call-by-value
take 2 (makefrom 4 5)
= take 2 (5 :: makefrom (4-1) (5+1))
= take 2 (5 :: makefrom 3 (5+1))
= take 2 (5 :: makefrom 3 6)
= take 2 (5 :: 6 :: makefrom (3-1) (6+1))
= take 2 (5 :: 6 :: makefrom 2 (6+1))
= take 2 (5 :: 6 :: makefrom 2 7)
= take 2 (5 :: 6 :: 7 :: makefrom (2-1) (7+1))
= take 2 (5 :: 6 :: 7 :: makefrom 1 (7+1))
= take 2 (5 :: 6 :: 7 :: makefrom 1 8)
= take 2 (5 :: 6 :: 7 :: 8 :: makefrom (1-1) (8+1))
= take 2 (5 :: 6 :: 7 :: 8 :: makefrom 0 (8+1))
= take 2 (5 :: 6 :: 7 :: 8 :: makefrom 0 9)
= take 2 (5 :: 6 :: 7 :: 8 :: [])
= 5 :: take (2-1) (6 :: 7 :: 8 :: [])
= 5 :: take 1 (6 :: 7 :: 8 :: [])
= 5 :: 6 :: take (1-1) (7 :: 8 :: [])
= 5 :: 6 :: take 0 (7 :: 8 :: [])
= 5 :: 6 :: []

View file

@ -0,0 +1,10 @@
Exercise #3:
Write a function named cond that has the same behavior as OCamls
if-then-else construct.
let cond a b c = match a with
| True -> b
| False -> c
let cond a b c = if a then b else c

View file

@ -0,0 +1,2 @@
# public-class-repo
Public course materials

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
# Resources
In this directory you will find various electronic resources that will
be used in the course.
#### OCaml
We will make use of Jason Hickey's draft of a book that was not
eventually published. But it is still a rather good resource and it
is free. It is in the file `Intro-to-OCaml_Hickey.pdf`.
The article "OCaml for the Masses" by Yaron Minsky provides a
compelling discussion for using languages like OCaml in real world
applications where speed and correctness matter.

View file

@ -0,0 +1,57 @@
static const char *ident __attribute__((__unused__))
= "$HeadURL: https://bradley.csail.mit.edu/svn/repos/cilk/5.4.3/examples/fib.cilk $ $LastChangedBy: sukhaj $ $Rev: 517 $ $Date: 2003-10-27 10:05:37 -0500 (Mon, 27 Oct 2003) $";
/*
* Copyright (c) 1994-2003 Massachusetts Institute of Technology
* Copyright (c) 2003 Bradley C. Kuszmaul
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <cilk-lib.cilkh>
#include <stdlib.h>
#include <stdio.h>
int START_OF_CILK_CODE = 0;
cilk int fib(int n)
{
if (n < 2)
return (n);
else {
int x, y;
x = spawn fib(n - 1);
y = spawn fib(n - 2);
sync;
return (x + y);
}
}
cilk int main(int argc, char *argv[])
{
int n, result;
if (argc != 2) {
fprintf(stderr, "Usage: fib [<cilk options>] <n>\n");
Cilk_exit(1);
}
n = atoi(argv[1]);
result = spawn fib(n);
sync;
printf("Result: %d\n", result);
return 0;
}

View file

@ -0,0 +1,17 @@
(* From
http://www.cs.cornell.edu/courses/cs3110/2011sp/lectures/lec17-concurrency/concurrency.htm
*)
let prog1 n =
let result = ref 0 in
let f i =
for j = 1 to n do
let v = !result in
Thread.delay (Random.float 1.);
result := v + i;
Printf.printf "Value %d\n" !result;
flush stdout
done in
ignore (Thread.create f 1);
ignore (Thread.create f 2)

View file

@ -0,0 +1,21 @@
(* From
http://www.cs.cornell.edu/courses/cs3110/2011sp/lectures/lec17-concurrency/concurrency.htm
*)
let prog2 n =
let result = ref 0 in
let m = Mutex.create () in
let f i =
for j = 1 to n do
Mutex.lock m;
let v = !result in
Thread.delay (Random.float 1.);
result := v + i;
Printf.printf "Value %d\n" !result;
flush stdout;
Mutex.unlock m;
Thread.delay (Random.float 1.)
done in
ignore (Thread.create f 1);
ignore (Thread.create f 2)

View file

@ -0,0 +1,200 @@
(* This file contains a translation of the Standard ML functions found
in sections 1 and 2 of Chapter 8 of Chris Reade's book Elements of
Functional Programming into OCaml.
Note that these functions, just like those in Reade's book, will
not evaluate. They are meant to illustrate concepts of lazy
functional programming. Since Standard ML and OCaml use eager
evaluation these functions do not work. Thus, these have not
been loaded into OCaml and thus may have smaller erros in them.
This translation is provided only to remove the barrier of
translating from Standard ML when reading this chapter.
Sometimes additional functions or values that are not in Reade's
chapter 8 are defined (such as plus with the code from page 268) to
help clarify the examples in Reade.
*)
(* from page 266 *)
let f x = g (h x x)
let g x = 5
let rec h x y = h y x
let f x = g (h x x)
(* page 267 *)
snd (raise (Failure "undefined"), 5)
(* page 268 *)
let plus x y = x + y
let double x = plus x x
(* page 269 *)
let f x = E
(* page 270 *)
let rec f x = f x in f ()
let tl lst =
match lst with
| (a::x) -> x
| [] -> raise (Failure "tl of [] is undefined")
(* page 271 *)
let k5 x = 5
let k5pr (x, y) = 5
(* page 272 *)
let andf a b =
match a with
| true -> b
| false -> false
let orf a b =
match a with
| true -> true
| false -> b
(* pate 273 *)
let cond c x y =
match c with
| true -> x
| false -> y
let rec equal_lists l1 l2 = (* called just = in Reade *)
match l1, l2 with
| [], [] -> true
| (a::x), (b::y) -> a = b && equal_lists x y
| (a::x), [] -> false
| [], (b::y) -> false
type 'a bintree = Lf of 'a
| Nd of 'a bintree * 'a bintree
(* Note that Reade used "/\" instead of Nd and thus writes this
operator using infix notation instead of prefix notation. Thus,
where you read "t1 /\ t2" in Reade, we would write it as "Nd t1
t2". (Note that OCaml does support some user-defined infix
operators be we are not using that capability here.) *)
(* page 274 *)
let rec eqleaves t1 t2 = leavesof t1 = leavesof t2
and leavesof t =
match t with
| Lf x -> [x]
| Nd (t1, t2) -> leavesof t1 @ leavesof t2
(* page 276 *)
let rec exists p lst =
match lst with
| [] -> false
| a::x -> if p a then true else exists p x
let null l =
match l with
| [] -> true
| _ -> false
let rec accumulate f a lst = (* similar to fold_left *)
match lst with
| [] -> a
| b::x -> accumulate f (f a b) x
let rec reduce f a lst = (* similar to fold_right *)
match lst with
| [] -> a
| b::x -> f b (reduce f a x)
let cons h t = h :: t
let append x y = reduce cons y x
(* page 277 *)
let rec append2 x y = revonto y (rev x)
and rev x = revonto [] x
and revonto y x = accumulate consonto y x
and consonto y a = a :: y
let rec infbt1 = Nd (Lf 6, infbt1)
let rec infbt2 = Nd (Nd (infbt2, Lf 2), Nd (Lf 3, infbt1))
(* page 279 *)
let rec leftmostleaf t =
match t with
| Lf x -> x
| Nd (left, right) -> leftmostleaf left
let rec ones = 1 :: ones
(* page 280 *)
let rec from n = n :: from (n + 1)
let nat = from 1
let rec zip f lst1 lst2 =
match lst1, lst2 with
| [], [] -> []
| a::x, b::y -> f a b :: zip f x y
| _, _ -> raise (Failure "zipping lists of different lengths")
let rec nat = zip plus ones (0 :: nat)
(* page 281 *)
let rec factorials = 1 :: zip
(* page 282 *)
let rec sieve lst =
match lst with
| a::x -> a :: sieve (sift a x)
let primes = sieve (from 2)
let rec multipleof a b = b mod a = 0
and non p = fun x -> not p x
and sift a x = filter (non (multipleof a)) x
(* page 283 *)
let rec nextfind a b lst =
match lst with
| c::x -> if c < b then c :: nextfind a b x else
if c = b then nextfined a (a + b) x else
nextfind a (a + b) (c :: x)
let sift a x = netfind a (2 * a) x
(* page 287 *)
let rec merge2 lst1 lst2 =
match lst1, lst2 with
| a::x, b::y -> if a < b then a :: merge2 x (b::y) else
if a > b then b :: merge2 (a::x) y else
a :: merge2 x y
let merge3 x y z = merge2 x (merge2 y z)
let times a b = a * b
let rec hamming = 1 :: merge3 (map (times 2) hamming)
(map (times 3) hamming)
(map (times 5) hamming)

View file

@ -0,0 +1,5 @@
These examples using intervals are based on the examples in Chapters 4
and 9 of Real World OCaml.
Some small modifications have been made to explain modules, but they
are fundamentally the same as in the Real World OCaml book.

View file

@ -0,0 +1,36 @@
(* A module for intervals over integers.
This is not an abstract type, all types are exposed.
The module is used only to collect the types and values
into a single entity.
This code is based on the Interval examples in Chapter 9 of Real
World OCaml by Jason Hickey, Anil Madhavapeddy and Yaron Minsky.
*)
type intInterval = Interval of int * int
| Empty
let is_empty (i:intInterval) : bool =
match i with
| Empty -> true
| Interval _ -> false
let contains (i:intInterval) (x:int) : bool =
match i with
| Empty -> false
| Interval (l,h) -> l <= x && x <= h
let intersect (i1:intInterval) (i2:intInterval) : intInterval =
match i1, i2 with
| Empty, _ | _, Empty -> Empty
| Interval (l1, h1), Interval (l2, h2) ->
Interval (max l1 l2, min h1 h2)
let to_string (i:intInterval) : string =
match i with
| Empty -> "Empty"
| Interval (l,h) -> "(" ^ string_of_int l ^ ", " ^ string_of_int h ^ ")"

View file

@ -0,0 +1,13 @@
let i1 = IntInterval.Interval (3, 4)
let i2 = IntInterval.Interval (3, 6)
let () =
print_endline ("An interval: " ^ IntInterval.to_string i1) ;
print_endline ("Another interval: " ^ IntInterval.to_string i2) ;
print_endline ("Their intresection: " ^
IntInterval.to_string (IntInterval.intersect i1 i2)) ;

View file

@ -0,0 +1,41 @@
(* A module for intervals over integers.
Here, the type is abstract and hidden from users of the code because
the corresponding .mli file does not mention the type 'intInterval'.
Thus it is not visible since it is not in the interface for this
module.
This code is based on the Interval examples in Chapter 9 of Real
World OCaml by Jason Hickey, Anil Madhavapeddy and Yaron Minsky.
*)
type intInterval = Interval of int * int
| Empty
type t = intInterval
let create (low: int) (high:int) : t =
Interval (low, high)
let is_empty (i:intInterval) : bool =
match i with
| Empty -> true
| Interval _ -> false
let contains (i:intInterval) (x:int) : bool =
match i with
| Empty -> false
| Interval (l,h) -> l <= x && x <= h
let intersect (i1:intInterval) (i2:intInterval) : intInterval =
match i1, i2 with
| Empty, _ | _, Empty -> Empty
| Interval (l1, h1), Interval (l2, h2) ->
Interval (max l1 l2, min h1 h2)
let to_string (i:intInterval) : string =
match i with
| Empty -> "Empty"
| Interval (l,h) -> "(" ^ string_of_int l ^ ", " ^ string_of_int h ^ ")"

View file

@ -0,0 +1,15 @@
(* An interface file for the intInterval that hides the implementation
type.
*)
type t
val create : int -> int -> t
val is_empty : t -> bool
val contains : t -> int -> bool
val intersect : t -> t -> t
val to_string : t -> string

View file

@ -0,0 +1,37 @@
(* A sample use of the new IntInterval that hides the
implementation type.
*)
(* We now use the 'create' function instead of the hidden "Interval"
value constructor. *)
let i1 = IntInterval.create 3 4
let i2 = IntInterval.create 3 6
let () =
print_endline ("An interval: " ^ IntInterval.to_string i1) ;
print_endline ("Another interval: " ^ IntInterval.to_string i2) ;
print_endline ("Their intresection: " ^
IntInterval.to_string (IntInterval.intersect i1 i2)) ;
(* Try uncommenting out these lines to see if we really can't use the
Interval value constructor.
In utop, we (gasp!) can, if we "mod_use" the source file since this
ignores the .mli file.
When using the compiler, via corebuild, then IntInterval.Interval
is not defined. It is hidden. We only see the abstract type t.
The command
% corebuild useIntInterval.byte
shows the error.
*)
(*
let i3 = IntInterval.Interval (3, 4)
let i4 = IntInterval.Interval (3, 6)
*)

View file

@ -0,0 +1,43 @@
(* Defining the IntInterval module with an explicit signature as a
nested module.
*)
module IntInterval : sig
type t
val create : int -> int -> t
val is_empty : t -> bool
val contains : t -> int -> bool
val intersect : t -> t -> t
val to_string : t -> string
end = struct
type intInterval = Interval of int * int
| Empty
type t = intInterval
let create (low: int) (high:int) : t =
Interval (low, high)
let is_empty (i:intInterval) : bool =
match i with
| Empty -> true
| Interval _ -> false
let contains (i:intInterval) (x:int) : bool =
match i with
| Empty -> false
| Interval (l,h) -> l <= x && x <= h
let intersect (i1:intInterval) (i2:intInterval) : intInterval =
match i1, i2 with
| Empty, _ | _, Empty -> Empty
| Interval (l1, h1), Interval (l2, h2) ->
Interval (max l1 l2, min h1 h2)
let to_string (i:intInterval) : string =
match i with
| Empty -> "Empty"
| Interval (l,h) -> "(" ^ string_of_int l ^ ", " ^ string_of_int h ^ ")"
end

View file

@ -0,0 +1,23 @@
(* A sample use of the new IntInterval that hides the
implementation type.
To compile:
% corebuild useIntInterval.byte
*)
(* This 'open' makes visible all the names declared at the top level
of the Intervals modules, which is the contents of the intervals.ml
file.
*)
open Intervals
let i1 = IntInterval.create 3 4
let i2 = IntInterval.create 3 6
let () =
print_endline ("An interval: " ^ IntInterval.to_string i1) ;
print_endline ("Another interval: " ^ IntInterval.to_string i2) ;
print_endline ("Their intresection: " ^
IntInterval.to_string (IntInterval.intersect i1 i2)) ;

View file

@ -0,0 +1,14 @@
(* Here use use the 'Make_interval' functor to create a module
with types and functions for integer intervals. *)
open Intervals
module Int_interval =
Make_interval (
struct
type t = int
let compare = compare
let to_string = string_of_int
end )

View file

@ -0,0 +1,64 @@
(* This code is the same as in Chapter 9 of Real World OCaml,
except that we've added a 'to_string' function to the
signature and modules.
*)
(* We first create a signature, sometimes called an interface,
indicating what types and values a module must have, at least,
to be used as an end point in an interval.
*)
module type Comparable = sig
type t
val compare : t -> t -> int
val to_string : t -> string
end
(* The Make_interval functor takes a module that matches the
Comparable signature and uses it to create an interval module over
endpoint defined by that input module.
*)
module Make_interval(Endpoint : Comparable) = struct
type t = | Interval of Endpoint.t * Endpoint.t
| Empty
(* 'create low high' creates a new interval from 'low' to
'high'. If 'low > high', then the interval is empty *)
let create low high =
if Endpoint.compare low high > 0 then Empty
else Interval (low,high)
(* Returns true iff the interval is empty *)
let is_empty = function
| Empty -> true
| Interval _ -> false
(* 'contains t x' returns true iff 'x' is contained in the
interval 't' *)
let contains t x =
match t with
| Empty -> false
| Interval (l,h) ->
Endpoint.compare x l >= 0 && Endpoint.compare x h <= 0
(* 'intersect t1 t2' returns the intersection of the two input
intervals *)
let intersect t1 t2 =
let min x y = if Endpoint.compare x y <= 0 then x else y in
let max x y = if Endpoint.compare x y >= 0 then x else y in
match t1,t2 with
| Empty, _ | _, Empty -> Empty
| Interval (l1,h1), Interval (l2,h2) ->
create (max l1 l2) (min h1 h2)
let to_string i : string =
match i with
| Empty -> "Empty"
| Interval (l,h)
-> "(" ^ Endpoint.to_string l ^ ", " ^ Endpoint.to_string h ^ ")"
end

View file

@ -0,0 +1,16 @@
(* Here use use the 'Make_interval' functor to create a module
with types and functions for string intervals. *)
open Intervals
module String_interval =
Make_interval (
struct
type t = string
let compare = compare
let to_string x = x
end )

View file

@ -0,0 +1,23 @@
open IntInterval
open Intervals
open StringInterval
let i1 = Int_interval.create 3 4
let i2 = Int_interval.create 3 6
let s1 = String_interval.create "a" "d"
let () =
print_endline ("An interval: " ^ Int_interval.to_string i1) ;
print_endline ("Another interval: " ^ Int_interval.to_string i2) ;
print_endline ("Their intresection: " ^
Int_interval.to_string (Int_interval.intersect i1 i2)) ;
print_endline ("A string interval: " ^ String_interval.to_string s1)

View file

@ -0,0 +1,28 @@
open Intervals
module Int_interval = Make_interval(
struct
type t = int
let compare = compare
let to_string = string_of_int
end)
(* The following line causes an error.
We've provided 'create' the values of type 'int' and this is the
same as Int.t' which is the same as EndPoint.t.
But 'endpoint' in Int_interval is bound to the type
'Make_interval(Core.Std.Int).endpoint'
We've failed to indicate that the endpoint in Int_interval is
related to the type in the Int module that is input to the
Make_interval functor.
We also need to make sure this type is not hidden.
*)
let i = Int_interval.create 3 4

View file

@ -0,0 +1,71 @@
(* As before, we need to signature for endpoints. *)
module type Comparable = sig
type t
val compare : t -> t -> int
val to_string : t -> string
end
(* Now, define the signature that hides the type of the interval, named
t below, and the type of the end points, named endpoint.
*)
module type Interval_intf = sig
type t
type endpoint
val create : endpoint -> endpoint -> t
val is_empty : t -> bool
val contains : t -> endpoint -> bool
val intersect : t -> t -> t
val to_string : t -> string
end
(* The Make_interval functor takes a module that matches the
Comparable signature and uses it to create an interval module over
endpoint defined by that input module.
*)
module Make_interval(Endpoint : Comparable) : Interval_intf = struct
type endpoint = Endpoint.t
type t = | Interval of Endpoint.t * Endpoint.t
| Empty
(** [create low high] creates a new interval from [low] to
[high]. If [low > high], then the interval is empty *)
let create low high =
if Endpoint.compare low high > 0 then Empty
else Interval (low,high)
(** Returns true iff the interval is empty *)
let is_empty = function
| Empty -> true
| Interval _ -> false
(** [contains t x] returns true iff [x] is contained in the
interval [t] *)
let contains t x =
match t with
| Empty -> false
| Interval (l,h) ->
Endpoint.compare x l >= 0 && Endpoint.compare x h <= 0
(** [intersect t1 t2] returns the intersection of the two input
intervals *)
let intersect t1 t2 =
let min x y = if Endpoint.compare x y <= 0 then x else y in
let max x y = if Endpoint.compare x y >= 0 then x else y in
match t1,t2 with
| Empty, _ | _, Empty -> Empty
| Interval (l1,h1), Interval (l2,h2) ->
create (max l1 l2) (min h1 h2)
let to_string i : string =
match i with
| Empty -> "Empty"
| Interval (l,h)
-> "(" ^ Endpoint.to_string l ^ ", " ^ Endpoint.to_string h ^ ")"
end

View file

@ -0,0 +1,16 @@
open Intervals
module Int_comparable : (Comparable with type t = int) = struct
type t = int
let compare = compare
let to_string = string_of_int
end
module Int_interval = Make_interval(Int_comparable)
(* The following line now works. *)
let i = Int_interval.create 3 4

View file

@ -0,0 +1,78 @@
(* As before, we need to signature for endpoints. *)
module type Comparable = sig
type t
val compare : t -> t -> int
val to_string : t -> string
end
(* Now, define the signature that hide the type of the interval, named
t below, and the type of the end points, named endpoint.
*)
module type Interval_intf = sig
type t
type endpoint
val create : endpoint -> endpoint -> t
val is_empty : t -> bool
val contains : t -> endpoint -> bool
val intersect : t -> t -> t
val to_string : t -> string
end
(* The Make_interval functor takes a module that matches the
Comparable signature and uses it to create an interval module over
endpoint defined by that input module.
****
It also states the the 'endpoint' type in 'Interval_intf'
is the same as the type 't' in the 'Endpoint' module passed
in to 'Make_interval'
*****
*)
module Make_interval(Endpoint : Comparable) :
(Interval_intf with type endpoint = Endpoint.t) = struct
type endpoint = Endpoint.t
type t = | Interval of Endpoint.t * Endpoint.t
| Empty
(** [create low high] creates a new interval from [low] to
[high]. If [low > high], then the interval is empty *)
let create low high =
if Endpoint.compare low high > 0 then Empty
else Interval (low,high)
(** Returns true iff the interval is empty *)
let is_empty = function
| Empty -> true
| Interval _ -> false
(** [contains t x] returns true iff [x] is contained in the
interval [t] *)
let contains t x =
match t with
| Empty -> false
| Interval (l,h) ->
Endpoint.compare x l >= 0 && Endpoint.compare x h <= 0
(** [intersect t1 t2] returns the intersection of the two input
intervals *)
let intersect t1 t2 =
let min x y = if Endpoint.compare x y <= 0 then x else y in
let max x y = if Endpoint.compare x y >= 0 then x else y in
match t1,t2 with
| Empty, _ | _, Empty -> Empty
| Interval (l1,h1), Interval (l2,h2) ->
create (max l1 l2) (min h1 h2)
let to_string i : string =
match i with
| Empty -> "Empty"
| Interval (l,h)
-> "(" ^ Endpoint.to_string l ^ ", " ^ Endpoint.to_string h ^ ")"
end

View file

@ -0,0 +1,15 @@
open Intervals
module Int_comparable : (Comparable with type t = int) = struct
type t = int
let compare = compare
let to_string = string_of_int
end
module Int_interval = Make_interval(Int_comparable)
(* The following line now works. *)
let i = Int_interval.create 3 4

View file

@ -0,0 +1,81 @@
(* As before, we need to signature for endpoints. *)
module type Comparable = sig
type t
val compare : t -> t -> int
val to_string : t -> string
end
(* Now, define the signature that hide the type of the interval, named
t below, and the type of the end points, named endpoint.
*)
module type Interval_intf = sig
type t
type endpoint
val create : endpoint -> endpoint -> t
val is_empty : t -> bool
val contains : t -> endpoint -> bool
val intersect : t -> t -> t
val to_string : t -> string
end
(* The Make_interval functor takes a module that matches the
Comparable signature and uses it to create an interval module over
endpoint defined by that input module.
****
It also states the the 'endpoint' type in 'Interval_intf'
is replaced by hee type 't' in the 'Endpoint' module passed
in to 'Make_interval'
In this case we used := instead of = to indicate this destructive
substitution.
*****
*)
module Make_interval(Endpoint : Comparable) :
(Interval_intf with type endpoint := Endpoint.t) = struct
type endpoint = Endpoint.t
type t = | Interval of Endpoint.t * Endpoint.t
| Empty
(** [create low high] creates a new interval from [low] to
[high]. If [low > high], then the interval is empty *)
let create low high =
if Endpoint.compare low high > 0 then Empty
else Interval (low,high)
(** Returns true iff the interval is empty *)
let is_empty = function
| Empty -> true
| Interval _ -> false
(** [contains t x] returns true iff [x] is contained in the
interval [t] *)
let contains t x =
match t with
| Empty -> false
| Interval (l,h) ->
Endpoint.compare x l >= 0 && Endpoint.compare x h <= 0
(** [intersect t1 t2] returns the intersection of the two input
intervals *)
let intersect t1 t2 =
let min x y = if Endpoint.compare x y <= 0 then x else y in
let max x y = if Endpoint.compare x y >= 0 then x else y in
match t1,t2 with
| Empty, _ | _, Empty -> Empty
| Interval (l1,h1), Interval (l2,h2) ->
create (max l1 l2) (min h1 h2)
let to_string i : string =
match i with
| Empty -> "Empty"
| Interval (l,h)
-> "(" ^ Endpoint.to_string l ^ ", " ^ Endpoint.to_string h ^ ")"
end

View file

@ -0,0 +1,3 @@
# Sample Programs
Various sample OCaml programs will be placed here during the semester.

View file

@ -0,0 +1,18 @@
type expr
= Const of int
| Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
let rec eval (e:expr) : int =
match e with
| Const x -> x
| Add (e1, e2) -> eval e1 + eval e2
| Sub (e1, e2) -> eval e1 - eval e2
| Mul (e1, e2) -> eval e1 * eval e2
| Div (e1, e2) -> eval e1 / eval e2
let e1 = Add (Const 1, Mul (Const 2, Const 3))
let e2 = Sub (Const 10, Div (e1, Const 2))

View file

@ -0,0 +1,72 @@
(* from Feb 13 *)
type 'a tree = Leaf of 'a
| Fork of 'a * 'a tree * 'a tree
(* What are the pieces of the type definition above?
1. the name of the type constructor: tree
2. the arguments of the type constructor: 'a
3. the variants names: Leaf and Fork
these are also called value constructors
4. the values associated with those names
a. Leaf which holds a value of type 'a
b. Fork which holds a value and two sub trees.
*)
(* This is the 'monomorphic version - it only allows for integer trees
and is much less general than the 'tree' above.*)
type inttree = ILeaf of int
| IFork of int * inttree * inttree
let t1 = Leaf 5
let t2 = Fork (3, Leaf 3, Fork (2, t1, t1))
let t3 = Fork ("Hello", Leaf "World", Leaf "!")
let rec size t =
match t with
| Leaf _ -> 1
| Fork (_, ta, tb) -> 1 + size ta + size tb
let rec size' = fun t ->
match t with
| Leaf _ -> 1
| Fork (_, ta, tb) -> 1 + size ta + size tb
let rec sum = function
| Leaf v -> v
| Fork (v, t1, t2) -> v + sum t1 + sum t2
let rec tfold (l:'a -> 'b) (f:'a -> 'b -> 'b -> 'b) (t:'a tree) : 'b =
match t with
| Leaf v -> l v
| Fork (v, t1, t2) -> f v (tfold l f t1) (tfold l f t2)
let rec tmap (f:'a -> 'b) (t: 'a tree) : 'b tree = match t with
| Leaf v -> Leaf (f v)
| Fork (v, t1, t2) -> Fork (f v, tmap f t1, tmap f t2)
(* This is not the kind of folds that we want. It is better to have
folds the have a function (or value) for each value constructor
of the type. Thus `tfold` above has two functions, one for Leaf,
one for Fork.
So don't write functions like the one below, at least not for creating
general multi-purpose kinds of folding functions.
*)
let rec tfold' (f:'a -> 'b -> 'b) (accum:'b) (t:'a tree) : 'b =
match t with
| Leaf v -> f v accum
| Fork (v, t1, t2) -> let l' = tfold' f accum t1 in
let r' = tfold' f l' t2 in
f v r'

View file

@ -0,0 +1,197 @@
(* Continuation passing style is another mechanism for improving
performance.
Many of these are from Charlie Harper.
*)
type 'a tree = Empty
| Fork of 'a tree * 'a * 'a tree
let t =
Fork (
Fork (
Fork ( Empty, 1, Empty ),
2,
Fork ( Empty, 3, Empty )
),
4,
Fork (
Fork ( Empty, 5, Empty ),
6,
Fork ( Empty, 7, Empty )
)
)
let rec flatten t = match t with
| Empty -> []
| Fork (t1, v, t2) -> flatten t1 @ [v] @ flatten t2
(* How can we improve the performance of this function? *)
(* Here the extra parameter is not accumulating the result.
That is, we don't perform an operation on it directly.
It is a continuation for the result.
We keep adding to it.
*)
let flatten_c t =
let rec flatten_with t c = match t with
| Empty -> c
| Fork (t1, v, t2) -> flatten_with t1 (v :: flatten_with t2 c)
in
flatten_with t []
(* When we speak of "continuation passing style" we typically
mean passing a continuation that is a function.
*)
let ident x = x
let tail_fact n =
let rec tail_fact_rec n k = match n with
| 0 -> k 1
| _ -> tail_fact_rec (n-1) (fun r -> k (r*n))
in
tail_fact_rec n ident
exception InvalidArgument
(* This one is not quite so basic, but that's what you
get when linearizing traversals of bifurcating paths. *)
let tail_fib n =
let rec tail_fib_rec n k =
match n with
| 0 -> k 0
| 1 -> k 1
| n -> tail_fib_rec
(n - 1)
(fun rn1 ->
tail_fib_rec
(n - 2)
(fun rn2 ->
(k (rn1 + rn2))) )
in
if n >= 0
then tail_fib_rec n ident
else raise InvalidArgument
(*
tfr 3 id
tfr 2 c1
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
tfr 1 c2
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
where c2 = (\rn1 -> tfr 0 (\rn2 -> c1 (rn1 + rn2)))
c2 1
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
where c2 = (\rn1 -> tfr 0 (\rn2 -> c1 (rn1 + rn2)))
(\rn1 -> tfr 0 (\rn2 -> c1 (rn1 + rn2))) 1
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
tfr 0 (\rn2 -> c1 (1 + rn2))
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
tfr 0 c3
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
where c3 = (\rn2 -> c1 (1 + rn2))
c3 0
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
where c3 = (\rn2 -> c1 (1 + rn2))
(\rn2 -> c1 (1 + rn2)) 0
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
c1 (1 + 0)
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
c1 1
where c1 = (\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2)))
(\rn1 -> tfr 1 (\rn2 -> id (rn1 + rn2))) 1
tfr 1 (\rn2 -> id (1 + rn2))
(\rn2 -> id (1 + rn2)) 1
id (1 + 1)
2
*)
let sum_range f low high step =
let rec sum_range_rec x k =
if x <= high
then sum_range_rec (x + step) (fun r -> k (r + f x))
else k 0
in
if low > high
then raise InvalidArgument
else sum_range_rec low ident
(* Really useful for these, since the continuations allow the lists
and values to be constructed in the correct patterns, especially
for lists being recursively constructed non-reversed without
using list concatenation. *)
let rec map f lst =
match lst with
| [] -> []
| h::t -> f h :: map f t
(* Exercise: write a tail recursive version of map. *)
let map_c f lst =
let rec mc f lst k =
match lst with
| [] -> k []
| x::xs -> mc f xs (fun r -> k (f x :: r))
in
mc f lst ident
let tail_fold_right f lst v =
let rec tail_fr_rec l k =
match l with
| an::[] -> k (f an v)
| a::t -> tail_fr_rec t (fun r -> k (f a r))
| _ -> v in (* never goes down this branch *)
match lst with
| [] -> v
| _ -> tail_fr_rec lst ident
let tail_filter f lst =
let rec tail_filter_rec l k =
match l with
| [] -> k []
| h::t when f h -> tail_filter_rec t (fun r -> k (h::r))
| _::t -> tail_filter_rec t k in
tail_filter_rec lst ident
(* A rewrite of tail_filter showing how some of the business logic of
the problem can be moved into and out of the continuation. *)
let tail_filter2 f lst =
let rec tail_filter_rec l k =
match l with
| [] -> k []
| h::t ->
tail_filter_rec
t
(fun r -> if f h then k (h::r) else k r)
in
tail_filter_rec lst ident

View file

@ -0,0 +1,18 @@
type estring = char list
let rec explode = function
| "" -> []
| s -> String.get s 0 :: explode (String.sub s 1 ((String.length s) - 1))
let rec implode = function
| [] -> ""
| c::cs -> String.make 1 c ^ implode cs
(* Modifed from functions found at http://www.csc.villanova.edu/~dmatusze/8310summer2001/assignments/ocaml-functions.html *)
let freshperson cs =
let helper c = if c = '.' || c = '!' then '?' else c
in
map helper cs

View file

@ -0,0 +1,33 @@
type expr
= Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
| Lt of expr * expr
| Eq of expr * expr
| And of expr * expr
| Not of expr
| If of expr * expr * expr
| Let of string * expr * expr
| Id of string
| App of expr * expr
| Lambda of string * expr
| Value of value
and value
= Int of int
| Bool of bool
|
(* let add = fun x -> fun y -> x + y
let add x y = x + y *)
let add = Lambda ("x", Lambda( "y", Add (Id "x", Id "y")))
let inc = Lambda ("x", Add (Id "x", Value (Int 1)))
let two = App inc (Value (Int 1))

View file

@ -0,0 +1,36 @@
type expr
= Const of int
| Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
| Let of string * expr * expr
| Id of string
type environment = (string * int) list
let rec lookup (n:string) (env: environment) : int =
match env with
| [] -> raise (Failure ("Identifier " ^ n ^ " not in scope"))
| (n',v) :: rest when n = n' -> v
| _ :: rest -> lookup n rest
let rec eval (env: environment) (e:expr) : int =
match e with
| Const x -> x
| Add (e1, e2) -> eval env e1 + eval env e2
| Sub (e1, e2) -> eval env e1 - eval env e2
| Mul (e1, e2) -> eval env e1 * eval env e2
| Div (e1, e2) -> eval env e1 / eval env e2
| Let (n, dexpr, body) ->
let dexpr_v = eval env dexpr in
let body_v = eval ((n,dexpr_v)::env) body in
body_v
| Id n -> lookup n env
let e1 = Add (Const 1, Mul (Const 2, Const 3))
let e2 = Sub (Const 10, Div (e1, Const 2))
let e3 = Let ("x", Const 5, Add (Id "x", Const 4))
let e4 = Let ("y", Const 5, Let ("x", Add (Id "y", Const 5), Add (Id "x", Id "y")))

View file

@ -0,0 +1,18 @@
let rec filter f l =
match l with
| [] -> []
| x::xs ->
let rest = filter f xs
in
if f x
then x :: rest
else rest
let rec filter' f l =
match l with
| [] -> []
| x::xs when f x -> x :: filter f xs
| x::xs -> filter f xs
let filter_out f l = filter (fun v -> not (f v)) l

View file

@ -0,0 +1,57 @@
let m = [ ("dog", 1); ("chicken",2); ("dog",3); ("cat",5)]
let rec lookup_all s m =
match m with
| [] -> []
| (name,value)::ms ->
let rest = lookup_all s ms
in if s = name then value :: rest else rest
(* find all by : (a -> a ->bool) -> a ->
a list -> a list
*)
let streq s1 s2 = s1 = s2
let check (s2,v) s1 = s1 = s2
let rec find_all_by equals v lst =
match lst with
| [] -> []
| x::xs when equals x v -> x :: find_all_by equals v xs
| x::xs -> find_all_by equals v xs
let rec snds l =
match l with
| [] -> []
| (v1,v2)::rest -> v2 :: snds rest
let lookup_all' s m = snds (find_all_by check s m)
let is_elem_by equals e l =
match find_all_by equals e l with
| [] -> false
| _::_ -> true
let rec find_all_with f l =
match l with
| [] -> []
| x::xs ->
let rest = find_all_with f xs
in if f x then x::rest else rest
let find_all_by' eq v l = find_all_with (fun x -> eq x v) l
let find_all_by'' eq v = find_all_with (fun x -> eq x v)
let find_all_with' f l = find_all_by (fun x y -> f x = y) true
let find_all x = find_all_with ((=) x)
(* drop while: a list -> (a -> bool) -> a list
drop_while ( (=) 4 ) [4;4;4;5;6;7] = [5;6;7] *)
let rec drop_while l f =
match l with
| [] -> []
| v::rest -> if f v then drop_while rest f else v::rest

View file

@ -0,0 +1,50 @@
(* Sec 01, Feb 3 *)
(*
fold (+) 0 [1;2;3;4]
We can see this as
1 + (2 + (3 + (4 + 0)))
*)
let rec fold_v1 f e l =
match l with
| [] -> e
| x::xs -> f x (fold_v1 f e xs)
let f : (int -> string -> string) =
fun x -> fun s -> string_of_int x ^ ", " ^ s
(* fold (+) 0 [1;2;3;4]
as
((((0 + 1) + 2) + 3) + 4)
*)
let rec fold_v2 f e lst =
match lst with
| [] -> e
| hd::tail -> fold_v2 f (f e hd) tail
let g : (string -> int -> string) =
fun s -> fun x -> s ^ ", " ^ string_of_int x
(* Below are the versions of these functions from the slides. They
are the same excpet for their names. Below we chose 'l' and 'r' to
indicate if we are folding lists up from the left of from the
right. *)
let rec foldr (f:'a -> 'b -> 'b) (l:'a list) (v:'b) : 'b =
match l with
| [] -> v
| x::xs -> f x (foldr f xs v)
(* Notice how 'f' replaces '::' and 'v' replaces '[]' *)
let rec foldl f v l =
match l with
| [] -> v
| x::xs -> foldl f (f v x) xs
let length (lst: 'a list) : int = foldl (fun n _ -> n + 1 ) 0 lst

View file

@ -0,0 +1,92 @@
(*
Some sample evaluations from the exercises on Feb 6.
let x = 1 + 2 in let y = x + 3 in x + y
let x = 3 in let y = x + 3 in x + y
let x = 3 in let y = 3 + 3 in 3 + y
let x = 3 in let y = 6 in 3 + y
let x = 3 in let y = 6 in 3 + 6
let y = 6 in 3 + 6
3 + 6
9
let rec rev = function
| [] -> []
| x::xs -> rev xs @ [x]
rev (1::2::3::[])
rev (2::3::[]) @ [1]
(rev (3::[]) @ [2]) @ [1]
rev ([]) @ [3] @ [2] @ [1]
(([] @ [3]) @ [2]) @ [1]
([3] @ [2]) @ [1]
[3; 2] @ [1]
[3;2;1]
*)
type color = Red
| Green
| Blue
let isRed d =
match d with
Red -> true
| _-> false
type days = Sunday
| Monday
| Tuesday
| Wednesday
| Thursday
| Friday
| Saturday
let isWeekDay d =
match d with
| Sunday | Saturday -> false
| Monday | Tuesday | Wednesday | Thursday | Friday -> true
type intorstr = Int of int
| Str of string
(* this has the same functionality as the one in the slides *)
let rec sumList l =
match l with
| [] -> 0
| (Int i):: tl -> i + sumList tl
| (Str s):: tl -> sumList tl
(* The OCaml standard library offers the following type:
type 'a option = None
| Some of 'a
It is useful for so-called "optional" values. These are useful in
circumstances when a function may return a value, but it might not.
A function that is to read the contents of a file, for example. It
may be successful and return a string or char list, or the file
might not be found and thus it doesn't return a value.
*)

View file

@ -0,0 +1,56 @@
type expr
= Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
| Lt of expr * expr
| Eq of expr * expr
| And of expr * expr
| Not of expr
| If of expr * expr * expr
| Let of string * expr * expr
| Id of string
| Value of value
and value
= Int of int
| Bool of bool
(* freevars (Add(Id "x", Value (Int 3))) ---->> ["x"]
freevars (Let ("x", Id "y", (Add(Id "x", Value (Int 3))))) ---->> ["y"] *)
let rec freevars (e:expr) : string list =
match e with
| Value v -> []
| Add (e1, e2) -> freevars e1 @ freevars e2
| Let (i, dexpr, body) ->
freevars dexpr @ List.filter (fun i' -> i' <> i) (freevars body)
| Id i -> [i]
type environment = (string * value) list
let rec eval (env:environment) (e:expr) : value =
match e with
| Value v -> v
| Add (e1, e2) ->
( match eval env e1, eval env e2 with
| Int i1, Int i2 -> Int (i1 + i2)
| _, _ -> raise (Failure "incompatible type on Add")
)
| Sub (e1, e2) ->
( match eval env e1, eval env e2 with
| Int i1, Int i2 -> Int (i1 - i2)
| _, _ -> raise (Failure "incompatible type on Sub")
)
| Lt (e1, e2) ->
( match eval env e1, eval env e2 with
| Int i1, Int i2 -> Bool (i1 < i2)
| _, _ -> raise (Failure "incompatible type on Lt")
)
let e1 = Add (Value (Int 1), Sub (Value (Int 10), Value (Int 3)))

View file

@ -0,0 +1,165 @@
type value
= Int of int
| Bool of bool
type expr =
| Add of expr * expr
| Mul of expr * expr
| Sub of expr * expr
| Div of expr * expr
| Lt of expr * expr
| Eq of expr * expr
| And of expr * expr
| Var of string
| Value of value
type environment = (string * value) list
let rec lookup name env =
match env with
| [ ] -> raise (Failure ("Name \"" ^ name ^ "\" not found."))
| (k,v)::rest -> if name = k then v else lookup name rest
let rec eval (e: expr) (env: environment) : value =
match e with
| Value v -> v
| Add (e1, e2) ->
( match eval e1 env, eval e2 env with
| Int v1, Int v2 -> Int (v1 + v2)
| _ -> raise (Failure "incompatible types, Add")
)
| Sub (e1, e2) ->
( match eval e1 env, eval e2 env with
| Int v1, Int v2 -> Int (v1 - v2)
| _ -> raise (Failure "incompatible types, Sub")
)
| Mul (e1, e2) ->
( match eval e1 env, eval e2 env with
| Int v1, Int v2 -> Int (v1 * v2)
| _ -> raise (Failure "incompatible types, Mul")
)
| Div (e1, e2) ->
( match eval e1 env, eval e2 env with
| Int v1, Int v2 -> Int (v1 / v2)
| _ -> raise (Failure "incompatible types, Div")
)
| Lt (e1, e2) ->
( match eval e1 env, eval e2 env with
| Int v1, Int v2 -> Bool (v1 < v2)
| _ -> raise (Failure "incompatible types, Lt")
)
| Eq (e1, e2) ->
( match eval e1 env, eval e2 env with
| Int v1, Int v2 -> Bool (v1 = v2)
| Bool v1, Bool v2 -> Bool (v1 = v2)
| _ -> raise (Failure "incompatible types, Eq")
)
| And (e1, e2) ->
( match eval e1 env, eval e2 env with
| Bool v1, Bool v2 -> Bool (v1 && v2)
| _ -> raise (Failure "incompatible types, And")
)
| Var n -> lookup n env
type state = environment
type stmt =
| Assign of string * expr
| While of expr * stmt
| IfThen of expr * stmt
| IfThenElse of expr * stmt * stmt
| Seq of stmt * stmt
| WriteNum of expr
| ReadNum of string
(* x := 1;
y := x + 2;
z := y + 3;
write z
*)
let program_1 =
Seq ( Assign ("x", Value (Int 1)) ,
Seq ( Assign ("y", Add (Var "x", Value (Int 2))),
Seq ( Assign ("z", Add (Var "y", Value (Int 3))),
WriteNum (Var "z")
)
)
)
(* read x;
i = 0;
sum = 0;
while (i < x) {
write i;
sum = sum + i;
i = i + 1
}
write sum
*)
let program_2 =
Seq (ReadNum "x",
Seq (Assign ("i", Value (Int 0)),
Seq (Assign ("sum", Value (Int 0)),
Seq (While (Lt (Var "i", Var "x"),
Seq (WriteNum (Var "i"),
Seq (Assign ("sum", Add (Var "sum", Var "i")),
Assign ("i", Add (Var "i", Value (Int 1)))
) ) ),
WriteNum (Var "sum")
) ) ) )
(* program_3
read x;
i = 0;
sum_evens = 0;
sum_odds = 0;
while (i < x) {
write i;
if i mod 2 = 0 then
sum_evens = sum_evens + i;
else
sum_odds = sum_odds + i;
i = i + 1
}
write sum_evens;
write sum_odds
*)
let rec read_number () =
print_endline "Enter an integer value:" ;
try int_of_string (read_line ()) with
| Failure _ -> read_number ()
let write_number n = print_endline (string_of_int n)
let rec exec (s: stmt) (stt: state) : state =
match s with
| Assign (v, e) -> (v, (eval e stt)) :: stt
| Seq (s1, s2) -> exec s2 (exec s1 stt)
| WriteNum e -> (match eval e stt with
| Int v -> write_number v; stt
| _ -> raise (Failure "Only numeric values can be printed.")
)
| ReadNum v -> (v, Int (read_number ())) :: stt
| While (cond, body) ->
(match eval cond stt with
| Bool true -> exec (Seq (body, While (cond, body))) stt
(* the version in the Sec_10 directory is slighlty
different, but achieves the same results. *)
| Bool false -> stt
)

View file

@ -0,0 +1,46 @@
let is_empty_1 l =
if l = [] then true else false
let is_empty_2 l =
match l with
| [] -> true
| _ -> false
let is_empty3 l = l = []
let head l =
match l with
| [] -> None
| h::_ -> Some h
let head' l =
match l with
| [] -> raise (Failure "hey, genius, your list was empty")
| h::_ -> h
let rec drop_value td l =
match l with
| [] -> []
| h::t when h = td -> drop_value td t
| h::t -> h :: drop_value td t
let both_empty l1 l2 =
match (l1, l2) with
| ([], []) -> true
| (_, _ ) -> false
(*
Can you complete lookup_all?
let rec lookup_all v l =
match l with
| [] -> []
| (key,value)::rest when key = v -> ......
*)
let rec fib x =
match x with
| 0 -> 0
| 1 -> 1
| n -> fib(n-1) + fib(n-2)

View file

@ -0,0 +1,5 @@
let rec map f l =
match l with
| [] -> []
| x::xs -> f x :: map f xs

View file

@ -0,0 +1,6 @@
type nat = Zero | Succ of nat
let rec toInt n = match n with
| Zero -> 0
| Succ n' -> 1 + toInt n'

View file

@ -0,0 +1,30 @@
(* Construct a circular structure of the form
c --> 1 --> 2 --> 3
^ |
| |
+---------------+
Write a function that returns the first n elements.
Each number above should be a pair with an int and a reference to the
next pair.
*)
type box = Box of int * box ref
let rec dummy : box = Box (999, ref dummy)
let c =
let ref_in_one = ref dummy in
let one = Box (1, ref_in_one) in
let three = Box (3, ref one) in
let two = Box (2, ref three) in
let () = ref_in_one := two in
ref one
let rec c' =
Box (1, ref (Box (2, ref (Box (3, ref c')))))
let rec first_n (n:int) (b:box) : int list =
match n, b with
| 0, _ -> []
| _, Box (v, nb) -> v :: first_n (n-1) (!nb)

View file

@ -0,0 +1,199 @@
let s = [ 1; 3; -2; 5; -6 ] (* sample set from the S6 slides *)
let sum lst = List.fold_left (+) 0 lst
(* Now, is_elem which is used in processing the solution *)
let is_elem v l =
List.fold_right (fun x in_rest -> if x = v then true else in_rest) l false
let rec explode = function
| "" -> []
| s -> String.get s 0 :: explode (String.sub s 1 ((String.length s) - 1))
let rec implode = function
| [] -> ""
| c::cs -> String.make 1 c ^ implode cs
let show_list show l =
let rec sl l =
match l with
| [] -> ""
| [x] -> show x
| x::xs -> show x ^ "; " ^ sl xs
in "[ " ^ sl l ^ " ]"
(* ---
Exceptions
---
*)
(* We can also use exceptions in searching. This goes against the
general principle of only throwing an exception for truly
unexpected results, but it does make writing the code a bit more
convenient, so we will use them in this non-traditional way.
An exception is thrown when we've found the value that we want and
this quickly returns us to the top level where we can then report
success.
We now execute the two recursive calls to 'try_subset' in sequence,
not needing to inspect the output of the first one. If the first
call finds a solution then it will raise an exception. So we
don't care about the value returned by that first call. If it
returns it only does so if it didn't find a solution, in which case
we want to just keep searching.
*)
exception FoundSubSet of int list
(* OCaml's ";" expects a unit value on the left, so run evaluates an
expression but discards its result. This is used for expressions
that will throw an exception that we are planning to catch. This
run function is used to discard the value of e.
*)
let run e = (fun x -> ()) e
(* The subsetsum function that raises an exception on finding a
solution.
*)
let subsetsum_exn_on_found (lst: int list) : int list option =
let rec try_subset partial_subset rest_of_the_set =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then raise (FoundSubSet partial_subset)
else match rest_of_the_set with
| [] -> None
| x::xs -> run (try_subset (partial_subset @ [x]) xs) ;
try_subset partial_subset xs
in try try_subset [] lst with
| FoundSubSet (result) -> Some result
(* Another, and better, way to use exceptions in searching is to raise
an exception when we the search process has reached a deadend or
the found solution is not acceptable.
In both cases we want to keep looking. Thus we create a
"KeepLooking" exception.
*)
exception KeepLooking
(* In this example, we raise an exception when we reach a deadend in
the search process. This exception is caught in one of two places.
The first is at the point where there are more possibilities to
explore, and thus another call to try_subset is made.
The second is at the point where there are no more possibilities
and thus we catch teh exeption and return None.
*)
let subsetsum_exn_not_found (lst: int list) : int list option =
let rec try_subset partial_subset rest_of_the_set =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then Some partial_subset
else match rest_of_the_set with
| [] -> raise KeepLooking
| x::xs -> try try_subset (partial_subset @ [x]) xs with
| KeepLooking -> try_subset partial_subset xs
in try try_subset [] lst with
| KeepLooking -> None
(* In this example we again raise an exception to indicate that the
search process should keep looking for more solutions, but now we
use a version of the procss_solution function from above to have
some process (the user) that can reject found solutions causing the
function to keep searching.
*)
let rec process_solution_exn show s =
print_endline ( "Here is a solution:\n" ^ show s) ;
print_endline ("Do you like it?") ;
match is_elem 'Y' (explode (String.capitalize (read_line ()))) with
| true -> print_endline "Thanks for playing..." ; Some s
| false -> raise KeepLooking
(* This version of subsetsum is similar to subset_sum_option in that
it uses a version of process_solution to keep looking for more
solutions.
*)
let subsetsum_exn (lst: int list) : int list option =
let rec try_subset partial_subset rest_of_the_set =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then process_solution_exn (show_list string_of_int) partial_subset
else match rest_of_the_set with
| [] -> raise KeepLooking
| x::xs -> try try_subset (partial_subset @ [x]) xs with
| KeepLooking -> try_subset partial_subset xs
in try try_subset [] lst with
| KeepLooking -> None
(* We can abstract the subsetsub problem a bit more by parameterizing
by the function that is called when a candidate solution is found.
The function passed in is sometimes referred to as a "continuation"
as it indicates what the function should do, that is, how
processing should continue, after it has completed its work.
*)
let subsetsum_exn_continutation
(lst: int list) (success: int list -> int list option)
: int list option =
let rec try_subset partial_subset rest_of_the_set =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then success partial_subset
else match rest_of_the_set with
| [] -> raise KeepLooking
| x::xs -> try try_subset (partial_subset @ [x]) xs with
| KeepLooking -> try_subset partial_subset xs
in try try_subset [] lst with
| KeepLooking -> None
(* The function below has the same behavior as subsetsum_exn, but we
pass in process_solution_exn as an argument instead of writing it
explicitly in the body of the subsetsum function.
*)
let subsetsum_exn_v1 lst =
subsetsum_exn_continutation lst (process_solution_exn (show_list string_of_int))
(* This function has the same behavior as our original subsetsum
function that accepts the first solution. Here the continuation
function just wraps the result in a Some so that it can be
returned.
*)
let subsetsum_exn_first lst =
subsetsum_exn_continutation lst (fun x -> Some x)
let subsetsum_exn_print_all lst =
subsetsum_exn_continutation
lst
(fun s -> print_endline ("Here you go: " ^ (show_list string_of_int s)) ;
raise KeepLooking )
let results = ref [ ]
let subsetsum_exn_save_all lst =
subsetsum_exn_continutation
lst
(fun x -> results := x :: !results ;
print_endline (show_list (string_of_int) x) ;
raise KeepLooking)

View file

@ -0,0 +1,170 @@
(* NOTE: This file will be updated as the lectures cover more search
techniques. It is NOT COMPLETE as it now stands.
Below are the functions developed in lecture for the unit on search
as a programmin technique. The slides are S6_Search.
*)
(* Below, we generate all possible subsets of a set of values.
Note that we are using lists to represent sets and simply
assume that we do not have duplicates. If this is a concern
we could use a "dedup" function to remove duplicates.
The important point here is to see that for each element in the
list we have to make a choice - include it in the subset or do not
include it.
This leads to the two recursive calls, one that returns subsets
with it, and one that returns subsets without it.
See how the search tree we drew on the whiteboard corresponds to
the "call graph" of the functions?
*)
let gen_subsets lst
= let rec helper partial_subset rest
= match rest with
| [] -> [ partial_subset ]
| x::xs -> (helper (x :: partial_subset) xs)
@
(helper partial_subset xs)
in helper [] lst
(* using List.map, courtesy of Tiannan Zhou *)
let rec gen_subset' lst =
match lst with
| x::rest ->
(
let have_one = List.map (fun a -> x::a) (gen_subset' rest) in
let not_have_one = gen_subset' rest in
not_have_one @ have_one
)
| [ ] -> [[ ]]
(* ---
Options
---
*)
let s = [ 1; 3; -2; 5; -6 ] (* sample set from the S6 slides *)
let sum lst = List.fold_left (+) 0 lst
(* Our first implementation of subsetsum uses options to indicate if
we found a solution or not. If our searching function 'try_subset'
fails to find a value, it returns None; if it finds what we are
looking for, then it returns that values wrapped up in a Some.
*)
let subsetsum_v1 (lst : 'a list) : 'a list option
= let rec helper partial_subset rest
= if sum partial_subset = 0 && partial_subset <> []
then Some partial_subset
else
match rest with
| [] -> None
| x::xs -> match (helper (x :: partial_subset) xs) ,
(helper partial_subset xs) with
| Some solution, _ | (_, Some solution) -> Some solution
| None, None -> None
in helper [] lst
(* Here is another implementation of the above algorithm using
options. The final value returned by the function is an int list,
however. The empty list indicating that no subset was found.
The reason for writing this function is only to make it clear that
using an option in the return type of the subsetsum function above
was not related to our use of options in the recursive search
procedure.
*)
let subsetsum_option_v2 (lst: int list) : int list =
let rec try_subset partial_subset rest_of_the_set =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then Some partial_subset
else match rest_of_the_set with
| [] -> None
| x::xs -> match try_subset (partial_subset @ [x]) xs with
| None -> try_subset partial_subset xs
| Some result -> Some result
in match try_subset [] lst with
| None -> []
| Some result -> result
(* Below we see how we can keep searching once we've found a solution to
the problem.
It may be that this solution is not acceptable to the user or there
is simply some other evaluation criteria that we with to apply to
the found solution.
This lets the program keep looking even after finding a solution.
*)
(* First, a function for converting lists into strings *)
let show_list show l =
let rec sl l =
match l with
| [] -> ""
| [x] -> show x
| x::xs -> show x ^ "; " ^ sl xs
in "[ " ^ sl l ^ " ]"
(* Now, is_elem which is used in processing the solution *)
let is_elem v l =
List.fold_right (fun x in_rest -> if x = v then true else in_rest) l false
let rec explode = function
| "" -> []
| s -> String.get s 0 :: explode (String.sub s 1 ((String.length s) - 1))
let rec implode = function
| [] -> ""
| c::cs -> String.make 1 c ^ implode cs
(* We need to learn about modules soon ... S7 coming soon. *)
(* This function processes a solution, letting the user decide if
the solution is acceptable or not.
If not, then we want to keep looking. Thus, it returns None,
indicating that we have not yet found a solution, at least not one
that we want to keep.
If it is acceptable, then Some s (the proposed solution) is returned.
The function also takes a show function to print out the solution
to the user.
*)
let rec process_solution_option show s =
print_endline ("Here is a solution: " ^ show s) ;
print_endline ("Do you like it ?" ) ;
match is_elem 'Y' (explode (String.capitalize (read_line ()))) with
| true -> print_endline "Thanks for playing..." ; Some s
| false -> None
(* This version of subsetsum will let the user choose from the
discovered solutions, one at a time, until an acceptable one is
found.
The process_solution_optoin function returns None of a Some value
to indicate that the search should continue or end.
*)
let subsetsum_option (lst: int list) : int list option =
let rec try_subset partial_subset rest_of_the_set =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then (* Instead of returning Some partial_subset and quitting we let
the user decide to keep looking for more solutions or not. *)
process_solution_option (show_list string_of_int) partial_subset
else match rest_of_the_set with
| [] -> None
| x::xs -> match try_subset (partial_subset @ [x]) xs with
| None -> try_subset partial_subset xs
| Some result -> Some result
in try_subset [] lst

View file

@ -0,0 +1,9 @@
(* This is the filter function we wrote in class.
It differs a bit from the one in the file "streams.ml".
*)
let rec filter (f: 'a -> bool) (s: 'a stream) : 'a stream =
match s with
| Cons (h, t) -> if f h then Cons (h, (fun () -> filter f (t ())))
else filter f (t ())

View file

@ -0,0 +1,114 @@
(* ---
Continuation Passing Style
---
*)
let rec explode = function
| "" -> []
| s -> String.get s 0 :: explode (String.sub s 1 ((String.length s) - 1))
(* First, a function for converting lists into strings *)
let show_list show l =
let rec sl l =
match l with
| [] -> ""
| [x] -> show x
| x::xs -> show x ^ "; " ^ sl xs
in "[ " ^ sl l ^ " ]"
(* Now is_elem which are used in processing the solution *)
let is_elem v l =
List.fold_right (fun x in_rest -> if x = v then true else in_rest) l false
(* We use a sum function below, so we'll bring in the needed functions
for that. *)
let sum xs = List.fold_left (+) 0 xs
(* Continuation passing style is a style of writing programs in which
the computation that happens after a function returns is packaged
as a function and passed to that function instead where it is
called directly.
In CPS, functions do not return. The computation that happens next
is passed along as an argument in the form of a continuation
function.
In the subsetsum problem we pass two continuations, one to evaluate
if we succeed and find a subset that sums to 0, and another one in
the case in which we fail and reach a deadend in the search
process.
*)
let rec process_solution_cps_v1 show s succ fail =
print_endline ("Here is a solution: \n" ^ show s) ;
print_endline ("Do you like it?");
match is_elem 'Y' (explode (String.capitalize (read_line ()))) with
| true -> succ ()
| false -> fail ()
let rec try_subset_cps_v1 partial_subset rest_of_the_set succ fail =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then process_solution_cps_v1 (show_list string_of_int)
partial_subset succ fail
else match rest_of_the_set with
| [] -> fail ()
| x::xs ->
try_subset_cps_v1
(partial_subset @ [x]) xs
succ
(fun () -> try_subset_cps_v1 partial_subset xs succ fail)
(* Here the failure continuation will try to other
possibility of not including x in the partial subset.*)
let subsetsum_cps_v1 (lst: int list) =
try_subset_cps_v1
[] lst
(* Our success and failure continuations just print a message. *)
(fun () -> print_endline "Yeah, we found one")
(fun () -> print_endline "Oh no, no subset found.")
(* Another version in which the success continuation takes the chosen
subset as an argument.
*)
let rec process_solution_cps_v2 show s succ fail =
print_endline ( "Here is a solution:\n" ^ show s) ;
print_endline ("Do you like it?") ;
match is_elem 'Y' (explode (String.capitalize (read_line ()))) with
| true -> succ s
| false -> fail ()
let rec try_subset_cps_v2 partial_subset rest_of_the_set succ fail =
if sum partial_subset = 0 && partial_subset <> [] && rest_of_the_set = []
then process_solution_cps_v2 (show_list string_of_int)
partial_subset succ fail
else match rest_of_the_set with
| [] -> fail ()
| x::xs ->
try_subset_cps_v2
(partial_subset @ [x]) xs
succ
(fun () -> try_subset_cps_v2 partial_subset xs succ fail)
let subsetsum_cps_v2 (lst: int list) =
try_subset_cps_v2
[] lst
(fun ss -> print_endline ("Yeah, we found one.\n" ^
"It is as folows:\n" ^
show_list string_of_int ss))
(fun () -> print_endline "Oh no, no subset found.")

View file

@ -0,0 +1,182 @@
(* Some functions used in S4.3 Improving Performance
Some of this material comes from section 9.4 of Chris Reade's book
``Elements of Functional Programming''. This is in the `Resources`
directory of the public class repository.
Some were written by Charlie Harper.
*)
let rec listof n =
match n with
| 0 -> []
| _ -> n :: listof (n-1)
let rec append l1 l2 =
match l1 with
| [] -> l2
| x::xs -> x :: (append xs l2)
(* Problem 1: what is wrong with this function? *)
let rec rev lst =
match lst with
| [] -> []
| x::xs -> append (rev xs) [x]
(* Problem 2: how can these be improved? *)
let rec length lst = match lst with
| [] -> 0
| _::xs -> 1 + length xs
let rec sumlist lst = match lst with
| [] -> 0
| x::xs -> x + sumlist xs
(*- stacking up the additions
Evaluation - if delay additions a bit
sumlist 1::2::3::[]
1 + (sumlist 2::3::[])
1 + (2 + (sumlist 3::[])
1 + (2 + (3 + sumlist [])
1 + (2 + (3 + 0))
*)
(* Some solutions
--------------
*)
(* Use an accumulating parameter to convert reverse from
quadradic to linear time. *)
let rev_a lst =
let rec revaccum lst accum =
match lst with
| [] -> accum
| x::xs -> revaccum xs (x::accum)
in
revaccum lst []
(* Does the above function remind us of imperative programming?
accum = [] ;
while lst <> [] do
x::xs = lst
lst = xs
accum = x::accum
*)
let length_tr lst =
let rec ltr lst accum =
match lst with
| [] -> accum
| x::xs -> ltr xs (accum + 1)
in
ltr lst 0
(* We can also avoid stacking up the additions by using
an accumulating parameter. *)
let sumlist_a lst =
let rec accsum lst n =
match lst with
| [] -> n
| x::xs -> accsum xs (n+x)
in
accsum lst 0
(*
We delayed the additions - so not really call by value
sumlist_a [1;2;3]
accsum [1;2;3] 0
accsum [2;3] (0+1)
accsum [3] ((0+1)+2)
accsum [] (((0+1)+2)+3)
(((0+1)+2)+3)
*)
(* Exercise: what is the tail recurive version of length? *)
(* Fibonacci numbers *)
let rec fib n = match n with
| 0 -> 0
| 1 -> 1
| n -> fib (n-1) + fib (n-2)
(* What is the tail recursive version of the fib function
that uses accumulators to avoid all the recomputation?
*)
let fib_tr n =
let rec fib_acc a b n = match n with
| 0 -> a
| n -> fib_acc b (a+b) (n-1)
in
fib_acc 0 1 n
(* Another exercise: how does this relate to the imperative
version? *)
(* Let's recall sumlist and sumlist_tr.
Haven't we seen this before? *)
let rec foldl f v l =
match l with
| [] -> v
| x::xs -> foldl f (f v x) xs
let sum_f xs = foldl (+) 0 xs
(*-
foldl (+) 0 (1::2::3::[])
foldl (+) (0+1) (2::3::[])
foldl (+) 1 (2::3::[])
foldl (+) (1+2) (3::[])
foldl (+) 3 (3::[])
foldl (+) (3+3) []
foldl (+) 6 []
6
Or without evaluating + so early
foldl (+) 0 (1::2::3::[])
foldl (+) (0+1) (2::3::[])
foldl (+) ((0+1)+2) (3::[])
foldl (+) (((0+1)+2)+3) []
(((0+1)+2)+3)
6
*)

View file

@ -0,0 +1,117 @@
(* More tail recurive tree functions.
These are from Charlie Harper and over a differnt type of tree.
*)
type 'a tree = Leaf of 'a
| Fork of 'a * 'a tree * 'a tree
let t1 = Leaf 5
let t2 = Fork (3, Leaf 3, Fork (2,t1,t1))
let t3 = Fork ("Hello", Leaf "World", Leaf "!")
let ident = (fun x -> x)
let t_size t =
let rec t_size_rec t k =
match t with
| Leaf _ -> k 1
| Fork (_,tl,tr) ->
t_size_rec tl (fun l ->
t_size_rec tr (fun r ->
k (1 + l + r) ))
in
t_size_rec t ident
let t_sum t =
let rec t_sum_rec t k =
match t with
| Leaf v -> k v
| Fork (v,tl,tr) ->
t_sum_rec tl (fun l ->
t_sum_rec tr (fun r ->
k (v + l + r) ))
in
t_sum_rec t ident
let t_charcount t =
let rec t_charcount_rec t k =
match t with
| Leaf v -> k (String.length v)
| Fork (v,tl,tr) ->
t_charcount_rec tl (fun l ->
t_charcount_rec tr (fun r ->
k (l + r + String.length v) ))
in
t_charcount_rec t ident
let t_concat t =
let rec t_concat_rec t k =
match t with
| Leaf v -> k v
| Fork (v,tl,tr) ->
t_concat_rec tl (fun l ->
t_concat_rec tr (fun r ->
k (v ^ l ^ r) ))
in
t_concat_rec t ident
let t_elem_by (eq: 'a -> 'b -> bool) (elem: 'b) (t: 'a tree) : bool =
let rec t_elem_by_rec t k =
match t with
| Leaf v when eq v elem -> true
| Fork (v,_,_) when eq v elem -> true
| Leaf v -> k ()
| Fork (v,tl,tr) ->
t_elem_by_rec tl (fun u ->
t_elem_by_rec tr k)
in
t_elem_by_rec t (fun u -> false)
(* The ordering is left then fork value then right *)
(* t_to_list (Fork(1,Leaf 2,Leaf 3)) -> [2;1;3] *)
let t_to_list (t: 'a tree) : 'a list =
let rec t_to_list_rec t r k =
match t with
| Leaf v -> k (v::r)
| Fork (v,tl,tr) ->
t_to_list_rec tr r (fun r1 ->
t_to_list_rec tl (v::r1) k)
in
t_to_list_rec t [] ident
let tfold (l:'a -> 'b) (f:'a -> 'b -> 'b -> 'b) (t:'a tree) : 'b =
let rec tfold_rec t k =
match t with
| Leaf v -> k (l v)
| Fork (v,tl,tr) ->
tfold_rec tl (fun l ->
tfold_rec tr (fun r ->
k (f v l r) ))
in
tfold_rec t ident
(* A version of t_to_list that places the fork value first... *)
(* t_to_list (Fork(1,Leaf 2,Leaf 3)) -> [1;2;3] *)
let t_to_list_ff (t: 'a tree) : 'a list =
let rec t_to_list_rec t r k =
match t with
| Leaf v -> k (v::r)
| Fork (v,tl,tr) ->
t_to_list_rec tr r (fun r1 ->
t_to_list_rec tl r1 (fun r2 -> k (v::r2) ))
in
t_to_list_rec t [] ident
(* And a version that places the fork value last... *)
(* t_to_list (Fork(1,Leaf 2,Leaf 3)) -> [2;3;1] *)
let t_to_list_fl (t: 'a tree) : 'a list =
let rec t_to_list_rec t r k =
match t with
| Leaf v -> k (v::r)
| Fork (v,tl,tr) ->
t_to_list_rec tr (v::r) (fun r1 ->
t_to_list_rec tl r1 k)
in
t_to_list_rec t [] ident

View file

@ -0,0 +1,175 @@
(* Person, Wolf, Goat, Cabbage
Consider the problem of a person needing to move his wolf, goat,
and cabbage across a river in his canoe, under the following
restrictions:
- The canoe holds only the person and one of the wolf, goat, or
cabbage.
- The goat and cabbage cannot be left unattended or the goat will
eat the cabbage.
- The wolf and the goat cannot be left unattended or the wolf will
eat the goat.
- Only the person can operate the canoe.
Is there a sequence of moves in which the person can safely transport
all across the river with nothing being eaten?
*)
let rec is_not_elem set v =
match set with
| [] -> true
| s::ss -> if s = v then false else is_not_elem ss v
let run e = (fun x -> ()) e
let is_elem v l =
List.fold_right (fun x in_rest -> if x = v then true else in_rest) l false
let rec explode = function
| "" -> []
| s -> String.get s 0 :: explode (String.sub s 1 ((String.length s) - 1))
(* Types and functions for the crossing challenge. *)
(* Location: things are on the left (L) or right (R) side of the river. *)
type loc = L | R
(* A state in our search space is a configuration describing on which
side of the river the person, wolf, goat, and cabbage are. *)
type state = loc * loc * loc * loc
(* A state is safe, or OK, when the goat and cabbage are together only
when the person is also on the same side of the river and when the
wolf and the goat are together only when person is on the same side of
the river.
*)
let ok_state ( (p,w,g,c) :state) : bool
= p=g || (g <> c && g <> w)
(* The final state, or gaol state, is when everything is on the right (R)
side of the river.
*)
let final s = s = (R,R,R,R)
let other_side = function
| L -> R
| R -> L
let moves (s:state) : state list =
let move_person (p,w,g,c) = [ ( other_side p, w, g, c ) ]
in
let move_wolf (p,w,g,c) = if p = w
then [ ( other_side p, other_side w, g, c ) ]
else [ ]
in
let move_goat (p,w,g,c) = if p = g
then [ ( other_side p, w, other_side g, c ) ]
else [ ]
in
let move_cabbage (p,w,g,c) = if p = c
then [ ( other_side p, w, g, other_side c ) ]
else [ ]
in List.filter ok_state ( move_person s @ move_wolf s @ move_goat s @ move_cabbage s )
(* A solution using options that returns the first safe sequence of moves. *)
let crossing_v1 () =
let rec go_from state path =
if final state
then Some path
else
match List.filter (is_not_elem path) (moves state) with
| [] -> None
| [a] -> (go_from a (path @ [a]) )
| [a;b] ->
(match go_from a (path @ [a]) with
| Some path' -> Some path'
| None -> go_from b (path @ [b])
)
| _ -> raise (Failure ("No way to move 3 things!"))
in go_from (L,L,L,L) [ (L,L,L,L) ]
(* Here is a solution that raises an exception when we've found a safe
sequence of moves. It then stops.
*)
exception FoundPath of (loc * loc * loc * loc) list
let crossing_v2 () =
let rec go_from state path =
if final state
then raise (FoundPath path)
else
match List.filter (is_not_elem path) (moves state) with
| [] -> None
| [a] -> (go_from a (path @ [a]) )
| [a;b] ->
run (go_from a (path @ [a]) ) ;
go_from b (path @ [b])
| _ -> raise (Failure ("No way to move 3 things!"))
in try go_from (L,L,L,L) [ (L,L,L,L) ]
with FoundPath path -> Some path
(* A solution that allows use to keep looking for additional safe
sequences of moves.
*)
exception KeepLooking
(* This is the same process_solution_exn function from search.ml *)
let rec process_solution_exn show s =
print_endline ( "Here is a solution:\n" ^ show s) ;
print_endline ("Do you like it?") ;
match is_elem 'Y' (explode (String.capitalize (read_line ()))) with
| true -> print_endline "Thanks for playing..." ; Some s
| false -> raise KeepLooking
(* Some function for printint a sequence of moves. *)
let show_list show l =
let rec sl l =
match l with
| [] -> ""
| [x] -> show x
| x::xs -> show x ^ "; " ^ sl xs
in "[ " ^ sl l ^ " ]"
let show_loc = function
| L -> "L"
| R -> "R"
let show_state (p,w,g,c) =
"(" ^ show_loc p ^ ", " ^ show_loc w ^ ", " ^
show_loc g ^ ", " ^ show_loc c ^ ")"
let show_path = show_list show_state
(* The solution that lets a user selected from all (2) safe paths. *)
let crossing_v3 () =
let rec go_from state path =
if final state
then process_solution_exn show_path path
else
match List.filter (is_not_elem path) (moves state) with
| [] -> raise KeepLooking
| [a] -> go_from a (path @ [a])
| [a;b] ->
(try go_from a (path @ [a]) with
| KeepLooking -> go_from b (path @ [b])
)
| _ -> raise (Failure ("No way to move 3 things!"))
in try go_from (L,L,L,L) [ (L,L,L,L) ] with
| KeepLooking -> None

View file

@ -0,0 +1,20 @@
type expr
= Int of int
| Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
(* eval: expr -> int *)
let rec eval = function
| Int i -> i
| Add (e1, e2) -> eval e1 + eval e2
| Sub (e1, e2) -> eval e1 - eval e2
| Mul (e1, e2) -> eval e1 * eval e2
| Div (e1, e2) -> eval e1 / eval e2
(* 1 + 2 * 3 *)
let e1 = Add (Int 1, Mul (Int 2, Int 3))
let e2 = Sub (Int 10, Div (e1, Int 3))

View file

@ -0,0 +1,59 @@
(* from Feb 13 *)
(* What are the pieces of the type definition above?
1. the name of the type constructor: tree
2. the arguments of the type constructor: 'a
3. the variants names: Leaf and Fork
these are also called value constructors
4. the values associated with those names
a. Leaf which holds a value of type 'a
b. Fork which holds a value and two sub trees.
*)
(* tree is parametric polymorphic type *)
type 'a tree = Leaf of 'a
| Fork of 'a * 'a tree * 'a tree
let t1 = Leaf 5
let t2 = Fork (3, Leaf 1, Fork (6, t1, t1) )
let t3 = Fork ("Hello", Leaf "World", Leaf "!")
let rec tsize t =
match t with
| Leaf _ -> 1
| Fork (_, t1, t2) -> 1 + tsize t1 + tsize t2
let rec tsum = function
| Leaf v -> v
| Fork (v, t1, t2) -> v + tsum t1 + tsum t2
let rec tsum' = fun t ->
match t with
| Leaf v -> v
| Fork (v, t1, t2) -> v + tsum' t1 + tsum' t2
let rec tmap (eq:'a -> 'b) (t:'a tree) : 'b tree =
match t with
| Leaf x -> Leaf (eq x)
| Fork (x, t1, t2) -> Fork (eq x, tmap eq t1, tmap eq t2)
let rec tfold (l: 'a -> 'b) (f: 'a -> 'b -> 'b -> 'b) (t: 'a tree) : 'b =
match t with
| Leaf v -> l v
| Fork (v, t1, t2) -> f v (tfold l f t1) (tfold l f t2)
(* For Wednesday, use tfold to
1. write an identity function for trees
2. compute the number of characters in a string tree, such as t3
*)
(* inttree is a monomorphic type *)
type inttree = ILeaf of int
| IFork of int * inttree * inttree

Some files were not shown because too many files have changed in this diff Show more