Assignment 2. CS330 Programming Languages,
Summer 2005 (due June 8)
Printing a calendar (10% of grade)
Assignment Description
PLEASE READ CAREFULLY - MISSING
LITTLE DETAILS MIGHT COST YOU
In this assignment your task is to write a program in SML that
given a particular
year prints the calendar for that year with nicely formatted
text. The main
goal is to demonstrate the expressive power of list processing and
higher-order
functions over lists.
Part 1 (1 pt): Some support Functions
Write a function zipwith
f (x::xs,y::ys) that takes as input a function and
two lists and returns the list created by applying the function to the
respective items of each list. The two lists must have equal length
otherwise throw an exception. A few example will probably help:
- zipwith (fn (x,y) => x + y)
([1,2,3],[4,5,6]);
val it = [5,7,9]: int list
- zipwith (fn (x,y) => x ^ y) (["A","B","C"], ["a", "b", "c"]);
val it = ["Aa","Bb","Cc"]: string list
Write a function copy n i that
takes as argument an integer
and an item and creates a list with n copies of that item.
A few examples:
- copy 4 1;
val it = [1,1,1,1]: int list
- copy 4 #"a";
val it = [#"a",#"a",#"a",#"a"]: char list
You will also find the following builtin SML functions useful:
- length(l):
returns the length of a list
- @:
infix operator that appends two lists. For example [1,2,3]@[4,5] is
[1,2,3,4,5]. op@
is
the regular prefix version. For example op@([1,2,3], [4,5]) is
[1,2,3,4,5]
- print:
print a string to standard output
- implode:
convert a list of characters to a string
- explode:
convert a string to a list of characters
- Int.toString:
concerts an integer to a string
- List.take(l,n)
: take the first n items from list l
- List.drop(l,n)
: drop the first n item from list l
- mod : the usual mod infix operator
Part 2 (1pt): Character Pictures
The main idea will be to construct the calendar by abstracting the
process
of composition of "pictures" which will be rectangular regions of text.
A character
picture (cpic) is going to be represented as a list of lists of
characters. Therefore you
can define:
type cpic = char list list
val pic1:cpic = [[#"*",#"-",#"*"],
[#"-",#"*",#"-"]];
Define the following functions on character pictures using only
previously
defined functions and no explicit recursion:
- height
p:cpic : return the height of a picture
- width p:cpic
: return the width of a picture
- empty (h,w)
: return an empty picture (each item is the space character #" ")
of
height h and width w (hint: use the copy function)
- above(x:cpic,
y:cpic) : return a new cpic that is x
above y
- besides(x:cpic, y:cpic) :
return a new cpic that is x besides y
Some examples:
<>- height(pic1);
val it = 2: int
- width pic1;
val it = 3: int
- empty(3,2);
val it = [[#" ",#" "],[#" ",#" "],[#" ",#" "]] : char list list
- above(pic1,pic1);
val it = [[#"*",#"-",#"*"],[#"-",#"*",#"-"],[#"*",#"-",#"*"],[#"-",#"*",#"-"]]
- besides(pic1,pic1);
val it = [[#"*",#"-",#"*",#"*",#"-",#"*"],[#"-",#"*",#"-",#"-",#"*",#"-"]]
Part 3 (2pt): foldr1 and a few more functions
Consider the following function:
<>exception foldr1Empty
fun foldr1 f [] = raise foldr1Empty
| foldr1 f [x] = x
| foldr1 f (x::xs) = f(x, foldr1 f xs)
Explain in your code comments
what does this function do. How is
it different than foldr
? This function is in curried form which means
you can use it to define new functions by calling it with one argument.
For example:
- val sum = foldr1 op+;
- sum [1,2,3,4];
val it = 10:int
Using this val notation and only calling foldr1 with one argument
define the following functions:
- stack
: takes a list of pictures and stacks them on top of each
other
- spread
: takes a list of pictures and spreads them next to each other
- display
: to display the picture you will need to insert a new line character
between each row and join all rows in one big list of characters (only
between). Then you can convert this list to a string using implode and print it to standard
output using print. To define
this function You are only allowed to use the function composition
operator o, print, implode and
foldr1 with an appropriately defined function.
Examples:
- display(stack [pic1,pic1,pic1]);
*-*
-*-
*-*
-*-
*-*
-*-val it = (): unit
- display(spread [pic1,pic1,pic1]);
*-**-**-*
-*--*--*-val it = () : unit
Part 4 (2pt): A few more support functions
Define a function group
n l that takes as input breaks a list l into
a list of lists of length n (hint: use List.take and List.drop)
For example:
- group 2 [1,2,3,4,5,6];
val it = [[1,2],[3,4],[5,6]] : int list list
- group 3 [1,2,3,4,5,6];
val it = [[1,2,3],[4,5,6]] : int list list
Define a function block
n l that creates a
block of cpics
from a list with length that is a multiple of n. Use only
function composition, map, stack, spread and group for
defining block.
For example:
- block 3 [pic1,pic1,pic1,pic1,pic1,pic1];
val it =
[[#"*",#"-",#"*",#"*",#"-",#"*",#"*",#"-",#"*"],
[#"-",#"*",#"-",#"-",#"*",#"-",#"-",#"*",#"-"],
[#"*",#"-",#"*",#"*",#"-",#"*",#"*",#"-",#"*"],
[#"-",#"*",#"-",#"-",#"*",#"-",#"-",#"*",#"-"]] : cpic
-
Define a function scan
f b l that returns the list
[b, f(b,l1),f(f(b,l1), l2), ....]. For example:
scan op+ 0 [1,2,3,4,5]
is
[0,1,3,6,10,15]
scan (fn(x,y) => x+2*y) 5 [1,2,3,4,5]
is
[5,7,11,17,25,35]
Scan is very similar to the foldl, foldr functions. The difference
is that it stores all the intermediate results in a list.
Part 5 (4pt): Finally let's do the calendar (this part is more
challenging)
The final code is to create a function calendar that given
a particular year displays the entire calendar with months and
days for that year. Each month will be a cpic for 10x25 lines
and each row of the calendar will contain 3 months. The whole
process will be constructed based on the functions you
have defined above in addition to a few helper functions.
Write a function lframe
(m,n) p that creates a cpic of m by n
dimensions with cpic p on the left corner and the rest
being empty (hint: use above, besides and empty).
For example:
- lframe (4,4) pic1;
val it =
[[#"*",#"-",#"*",#" "],[#"-",#"*",#"-",#" "],[#" ",#" ",#" ",#" "],
[#" ",#" ",#" ",#" "]] : char list list
Using lframe write
a function title
mn yr that takes
as input a month name and year and outputs the month
followed by the year framed in a 2x25 cpic.
For example:
- title (explode("January")) 1998;
val it =
[[#"J",#"a",#"n",#"u",#"a",#"r",#"y",#" ",#"1",#"9",#"9",#"8",...],
[#" ",#" ",#" ",#" ",#" ",#" ",#" ",#" ",#" ",#" ",#" ",#" ",...]]
: char list list
- display(title (explode("January")) 1998);
January 1998
val it = () : unit
-
The next step is to create a function table fd ml that takes
as input the first day of the month with Sunday being
0, Monday 1 etc and output an 8x25 cpic with the table of dates.
For example:
- display(table 0 30);
Sun 1 8 15 22 29
Mon 2 9 16 23 30
Tus 3 10 17 24
Wed 4 11 18 25
Thu 5 12 19 26
Fri 6 13 20 27
Sat 7 14 21 28
val it = () : unit
- display(table 1 29);
Sun 7 14 21 28
Mon 1 8 15 22 29
Tus 2 9 16 23
Wed 3 10 17 24
Thu 4 11 18 25
Fri 5 12 19 26
Sat 6 13 20 27
val it = () : unit
-
So basically the table is composed of smaller
cpics with 3 characters. You can implement
table anyway you want but you might find
the following functions helpful although you will
need to discover how.
fun rjustify3 n = let val ln = size(Int.toString(n))
in
copy (3-ln) #" " @ explode(Int.toString(n))
end
fun date ml d = if (d < 1) orelse (d > ml) then [explode(" ")]
else [rjustify3 d]
fun dates fd ml = map (date ml) (List.tabulate(42, (fn x => x - fd + 1)))
Now based on title
and table write
month (mn, yr, fd,
ml) that
takes as input the month, year, first day and month length and
returns the corresponding cpic.
For example:
- display(month ((explode "January"),1990,2,30));
January 1990
Sun 6 13 20 27
Mon 7 14 21 28
Tus 1 8 15 22 29
Wed 2 9 16 23 30
Thu 3 10 17 24
Fri 4 11 18 25
Sat 5 12 19 26
val it = () : unit
-
The next step is to construct the full calendar. Your calendar
function should take as argument the year and return the
full calendar. It should be written as a composition
of functions you have defined. For example:
- calendar 2005;
January 2005 February 2005 March 2005
Sun 2 9 16 23 30 Sun 6 13 20 27 Sun 6 13 20 27
Mon 3 10 17 24 31 Mon 7 14 21 28 Mon 7 14 21 28
Tus 4 11 18 25 Tus 1 8 15 22 Tus 1 8 15 22 29
Wed 5 12 19 26 Wed 2 9 16 23 Wed 2 9 16 23 30
Thu 6 13 20 27 Thu 3 10 17 24 Thu 3 10 17 24 31
Fri 7 14 21 28 Fri 4 11 18 25 Fri 4 11 18 25
Sat 1 8 15 22 29 Sat 5 12 19 26 Sat 5 12 19 26
April 2005 May 2005 June 2005
Sun 3 10 17 24 Sun 1 8 15 22 29 Sun 5 12 19 26
Mon 4 11 18 25 Mon 2 9 16 23 30 Mon 6 13 20 27
Tus 5 12 19 26 Tus 3 10 17 24 31 Tus 7 14 21 28
Wed 6 13 20 27 Wed 4 11 18 25 Wed 1 8 15 22 29
Thu 7 14 21 28 Thu 5 12 19 26 Thu 2 9 16 23 30
Fri 1 8 15 22 29 Fri 6 13 20 27 Fri 3 10 17 24
Sat 2 9 16 23 30 Sat 7 14 21 28 Sat 4 11 18 25
July 2005 August 2005 September 2005
Sun 3 10 17 24 31 Sun 7 14 21 28 Sun 4 11 18 25
Mon 4 11 18 25 Mon 1 8 15 22 29 Mon 5 12 19 26
Tus 5 12 19 26 Tus 2 9 16 23 30 Tus 6 13 20 27
Wed 6 13 20 27 Wed 3 10 17 24 31 Wed 7 14 21 28
Thu 7 14 21 28 Thu 4 11 18 25 Thu 1 8 15 22 29
Fri 1 8 15 22 29 Fri 5 12 19 26 Fri 2 9 16 23 30
Sat 2 9 16 23 30 Sat 6 13 20 27 Sat 3 10 17 24
October 2005 November 2005 December 2005
Sun 2 9 16 23 30 Sun 6 13 20 27 Sun 4 11 18 25
Mon 3 10 17 24 31 Mon 7 14 21 28 Mon 5 12 19 26
Tus 4 11 18 25 Tus 1 8 15 22 29 Tus 6 13 20 27
Wed 5 12 19 26 Wed 2 9 16 23 30 Wed 7 14 21 28
Thu 6 13 20 27 Thu 3 10 17 24 Thu 1 8 15 22 29
Fri 7 14 21 28 Fri 4 11 18 25 Fri 2 9 16 23 30
Sat 1 8 15 22 29 Sat 5 12 19 26 Sat 3 10 17 24 31
val it = () : unit
You can implement calendar anyway you want but you might find
the following functions helpful although you will
need to discover how:
fun zip4 ([],[],[],[]) = []
| zip4 (x::xs,y::ys,z::zs,w::ws) = (x,y,z,w)::zip4 (xs,ys,zs,ws)
val mnames = map explode ["January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"];
fun leapYear yr = if (yr mod 100 = 0) then (yr mod 400 = 0)
else (yr mod 4 = 0);
fun jan1 yr = (yr + (yr -1) div 4 - (yr -1) div 100 + (yr-1) div 400) mod 7;
I hope you find this assignment a good learning experience
and enjoyable.
EXTRA CREDIT
- (1pt) Implement calendar
in another programming language. All the functionality must be
implemented in order to get the 1 point.
- (1pt) Produce a nicely
formatted html calendar as output of the program. By this I don't mean
just using the existing output but
modifying the program structure so that it displays html elements.
<>(2pt) Use a similar
functional approach to create composable html tables of photographs.
For example you should be able to do:
stack("homer.jpg", "lisa.jpg", "bart.jpg");
>
Practical matters
You must submit the full
source code (including the functions I provide) in
a single .sml file.
Please don't use any archiving/compressing software (such as winzip,
rar, tar, gzip etc).
Check the course web page for submission instructions (should be
similar to the web submission system you have used for
other courses) and if you run into problems email me and we will figure
it out.
Late assignment policy: If the assignment is submitted
within three days from when it was due, you will get half the grade you
would get if you had submitted on time. You will not be able
to submit after three days. (exceptions to this rule only
if you have a VERY important reason).
IMPORTANT: The careful design
and documentation (i.e comments) of
your code will be important factors in your grade. If there is a bug
it's better to report it than hide it. Be honest, precise and clear and
you shall be rewarded. NEVER (at least in this class) sacrifice
clarity for efficiency of execution.