CSCI 330: User-Defined Variant Types in OCaml
Goals
- to continue our journey with OCaml
- to gain hands-on experience with Variants and Recursive Types
- to generate pretty cool images
Overview
You have been tasked with implementing several functions in OCaml. All of the programs require relatively little coding ranging from 2 to 15 lines. If any function requires more than that, you will need to rethink your solution.
- Download the handout zip archive from Autolab
- Update each of the functions with your own solutions. As a reminder, you are expected to complete this individually.
- Test your program using the OCaml environment installed on the Linux Lab machines
- Submit your updated handout file to the “Lab 4” Autolab assignment.
Start this assignment early I cannot emphasize this enough! ML programming is relatively easy (syntax-wise) but it will seem foreign, especially with recursion and list manipulation.
Note: Solutions using imperative features such as references, arrays, or while loops will receive no credit. Do not use any additional libraries, just use standard OCaml as installed in the Linux environment.
If you wish to work on your own computer for this lab, you will need to install Image Magick
- using ArchLinux:
sudo pacman -S imagemagick
- using Ubuntu:
sudo apt install imagemagick
- using macOS + brew:
brew install imagemagick
You are required to implement each of the following functions:
Expression component
Expressions are described with the following grammar:
e ::= x
| y
| sin (pi * e)
| cos (pi * e)
| (e + e) / 2
| e * e
| (e < e ? e : e)
Where pi is defined for you in expr.ml
.
NOTE: assuming the range of values for x
and
y
are [-1,1]
the guaranteed values for all
evaluated functions will also be [-1,1]
.
We define the custom datatype in OCaml as:
type expr =
VarX
| VarYof expr
| Sine of expr
| Cosine of expr * expr
| Average of expr * expr
| Times of expr * expr * expr * expr;; | Thresh
exprToString
(20
points)
Enables the “printing” of expressions.
Signature:
string expr ->
Expected Output
exprToString (Thresh(VarX,VarY,VarX,(Times(Sine(VarX),Cosine(Average(VarX,VarY))))));;
(* string = "(x<y?x:sin(pi*x)*cos(pi*((x+y)/2)))" *)
eval
(20 points)
Evaluates an expr
at a point (x,y)
Signature:
float * float -> float expr *
Expected Output
0.5,-0.5);;
eval (Sine(Average(VarX,VarY)),
(* float = 0.0 *)
0.3,0.3);;
eval (Sine(Average(VarX,VarY)),
(* float = 0.809016994375 *)
Hints
- You should use OCaml Math library, in particular
sin
andcos
to build your evaluator. - All expressions are floating-point
- Recall from writing
exprToString
thatSine(VarX)
corresponds to the expressionsin(pi*x)
.
Generating an Image
In your OCaml interpreter, enter the following two commands:
#use "art.ml";;
150, "art_g_sample") ;; emitGrayscale (eval_fn sampleExpr,
This will generate a grayscale image named
art_g_sample.jpg
in your working directory. To
receive full credit, this image must look like the following grayscale
image.
Note that this requires your implementations of eval to work
correctly. A message of Uncaught exception ...
is an
indication that your eval is returning a value outside the range
[-1.0, 1.0]
.
build
(30 points)
A random expression generator which requires a tuple of
(rand, depth)
Signature:
int * int -> int) * int -> expr (
The first element, rand
, is a random number generator of
type int * int -> int
- Each call to
rand (i,j)
returns a random integer betweeni
inclusive andj
exclusive. - Use this function to randomly select operators when composing subexpressions to build up larger expressions.
- An appropriate rand function for testing purposes can be generated
by calling
makeRand
(see comments inart.ml
).
The second element, depth, is a maximum nesting depth.
- A random expression of depth
d
should be built by randomly composing sub-expressions of depthd - 1
. - IMPORTANT The only expressions of depth
0
areVarX
orVarY
.
With this in place you can generate random art using the functions:
int * int * int -> unit
doRandomGray : int * int * int -> unit doRandomColor :
Each function takes as a parameter a triple
(depth, seed1, seed2)
where:
depth
is the depth of the expression to be generatedseed1
andseed2
are two seeds for the random number generator.- The functions generate JPEG files in your working directory
art_g_1_<depth>_<seed1>_<seed2>.jpg
art_c_1_<depth>_<seed1>_<seed2>.jpg
Hints and Notes
The gray scale image is built by mapping out a single randomly generated expression over the plane, and the color image is built using three functions for the intensities of red, green and blue.
Play around with how you generate the expressions, using the tips
below. Name your best color file color1.jpg
and your best
gray file gray1.jpg
. Save the parameters, i.e. the
depth, the seeds, and whether you used build or build2 to generate them
in the bodies of c1 and g1 (these are included in expr.ml for you to
fill in). These images will be used in the extra credit portion
of the assignment (voting for the coolest images).
- Don’t build the datatypes directly – use the build functions
provided in
expr.ml
. The purpose is to hide the actual implementation, i.e. the datatype, for the expressions from the random art code, thus enabling modular code. - Depths of 8-12 produce interesting pictures, but play around
- Make sure your expressions don’t get “cut-off” early with
VarX
andVarY
, as small expressions give simple pictures. In general, write your build function to bias the generation towards more interesting operators.
build2
(20 points)
- Add two new operators to the grammar, i.e. to the datatype, by:
- introducing two new datatype constructors, one of which
must accept three arguments
e.g.
C of expr * expr * expr
- adding the corresponding cases to
exprToString
. Give some meaningful name/representation. - adding the corresponding cases to
eval
. Ensure it mathematically evaluates to a range of[-1, 1]
- introducing two new datatype constructors, one of which
must accept three arguments
e.g.
- Write a new function
build2
, which behaves exactly like your functionbuild
, except which generates expressions including your two new operators. Test these functions. - When testing these functions, use
doRandomGray2
anddoRandomColor2
to generate the images.
Submission
Submit expr.ml
, gray1.jpg
, and
color1.jpg
as a ZIP FILE to the “Variant
Types” assignment through Autolab
You can create a zip file in linux by saying:
zip handin.zip expr.ml gray1.jpg color1.jpg
Grading
100 points total
- 70 points on passing the autolab tests
- 20 points on manual inspection for
build2
and required modifications - 10 points on documentation, formatting, describing what your code does, style, and adhering to the specifications and instructions listed
- NOTE points may be adjusted on the autograded component if you do not match the appropriate style/implementation requirements.
Attribution
This assignment was adapted from Sorin Lerner’s CSE 130 course at UCSD and slightly modified by Dr. Schwartz, Prof. Killian, and Dr. Zoppetti