CS 341 - Principles of Programming Languages
Homework #1 |
Note: This work is to be done in assigned groups. Each group will submit one assignment. Although you may divide the work, each team member should be able to independently present/describe all submitted work upon request.
NOTE: For each assignment, you should submit not only the requested program files,
but also a simple text file called README
.
README
files are often used by
programmers to comment on the contents of a directory or give vital information.
Please make sure this plain text file is named "README
", not "README.txt
",
"readme
", "ReadMe.txt
", "Read
me.doc
", etc. For each assignment, your README
file will contain:
Submission: I will have each group come to office hours and receive a new set of test input files (different from those below). You will generate the output with your HW1 assigned language during my office hours, and email me the following output files using these filenames from each of your programs as attachments:
For our 4th hour project this semester, you will be creating your own interpreted programming language. Before you create your language grammar, an important precursor step is to gain exposure to a variety of languages in order to inform you design. For this assignment, you will:
The 6 algorithms you will implement will be strictly and simply defined in a programming-contest-style input/output specification where input and output will consist of only one integer per line. These exercises range in difficulty from the level of a first introductory programming course to the level of a data structures course. For each exercise, you'll be provided with a single test input and solution output set of text files. You should create and use additional tests. Knowledge of Unix standard input/output redirection ("<" / ">") and "diff" and "wc" commands will be helpful in your testing. It is also worth your while to create script files to somewhat automate your testing. Good testing is key. All test files below may be downloaded in a single .zip file here.
NOTE: To the extent that it is possible with your language, you should (1) read integers from STANDARD INPUT, and (2) write only integers to STANDARD OUTPUT. If your language is not suited for this (e.g. JavaScript), meet with me to plan an alternative for I/O.Example testing done with Scheme from the terminal window:
$ plt-r5rs unique.scm < int-list-in.txt > my-unique-out.txt $ diff unique-out.txt my-unique-out.txt $ wc unique-out.txt my-unique-out.txt 60 60 198 unique-out.txt 60 60 198 my-unique-out.txtFrom the bash shell, the first command uses "<" to redirect the contents of the filename following (int-list-in.txt) to the standard input as if I were typing it in. It also uses ">" to redirect the standard output of my execution to the filename following (my-unique-out.txt). If I want to see if this is identical to the given correct output (unique-out.txt), I can use the "diff" command. With "diff", no news is good news: a lack of output means the files are the same, i.e. my output correctly matches the given output. I can also see how many lines, words, and characters the files contain using the "wc" (word count) command.
Input: a positive integer n, followed by n integers a1,
..., an (E.g.
int-list-in.txt)
Output: the maximum of the n integers a1,
..., an (E.g.
maximum-out.txt)
Input: a non-negative integer n (E.g.
factorial-in.txt)
Output: n factorial,
computed recursively (E.g.
factorial-out.txt)
Input: a positive integer n, followed by n integers a1,
..., an (E.g. int-list-in.txt)
Output: the unique integers of a1,
..., an ordered by first occurrence (E.g.
unique-out.txt)
Note: If the language has a standard representation of a set, use that. However, for the other 5 exercises beyond Unique, it is the implementation of the algorithms that aids in learning the language.
Input: a positive integer n, followed by n integers a1,
..., an (E.g.
int-list-in.txt)
Output: the integers of a1, ..., an,
sorted with the quicksort algorithm of Introduction to Algorithms 3rd
ed., by Cormen et al. (E.g.
quicksort-out.txt)
Input: a sequence of integer pairs, one integer per line. The first integer
of each pair will encode a binary tree operation (0=delete, 1=insert,
2=contains). The second integer of each pair will be the integer key for the
operation. After all pairs, a single value of -1 will indicate the end of input. (E.g.
binary-search-tree-in.txt)
Output: For each delete and contains operation, output 0/1 if the given value is
not/is currently contained within the binary tree, respectively. (E.g.
binary-search-tree-out.txt)
Note: The
binary search tree implementation will be according to the
binary search tree algorithms of
Introduction to Algorithms 3rd ed., by Cormen et al.
Input:
- a positive integer n, indicating the number of graph
nodes, indexed 0 through n-1
- pairs of node indices, one index per
line, indicating an undirected edge between the indexed nodes
- a value of
-1, indicating the end of the graph specification
- the start node index
u
- the goal node index v (E.g.
breadth-first-search-in.txt)
Output:
- a sequence of node
integers, beginning with u, ending with v, defining a graph
path from u to v computed with breadth-first search, or the
single integer -1 if no such path exists (E.g.
breadth-first-search-out.txt)
Note: The breadth-first search
implementation will be according to the breadth-first
search algorithms of Introduction to Algorithms
3rd ed., by Cormen et al. and makes use of variable-length adjacency
lists.
With each of these problems approached in sequence, there is a natural progression of questions to answer with each. Here are some examples:
This is not an exhaustive list of questions, of course, but in them you can see a gradual, step-by-step formation of general-purpose programming competency. These tasks get you started with basics, forming a sort of "pidgin" usage of the programming language but not necessarily providing understanding of important language idioms. For this reason, it is also important to study many good examples of programming idioms and express/apply them yourself to become truly fluent in a programming langauge. Expect each language to have strengths, specialty application areas, and stylistic merits that may not be obvious at first glance. If you seek to program every language in the style of Java programmers, you'll not only be unsatisfied with most everything but Java, but you'll also miss the beauty of other language design decisions and programming paradigms.
In approaching different programming languages, become like an international traveller that is able to become immersed in the culture to the point that there will be something missed and appreciated from each culture. For example, engage a functional programming language such as Scheme as Scheme programmers do, making liberal use of tail recursion and lesser use of loops, favoring thinking in terms of expression evaluation rather than statement execution, and you will grow to be a multi-paradigm programmer, able to discern the right programming approach to diverse problems.
One question to ask yourself for HW1 language learning: Does my programming language take care of garbage collection for me? That is, does it free up dynamically allocated memory for arrays and objects that I'm not using anymore?
If not, then when you are done with dynamically allocated memory (e.g. binary search tree nodes that are removed) one should free that memory explicitly.
In C, one can "free" memory allocated with "malloc". In C++, one needs to "delete" an object before you lose the last reference to it. Failure to free up unused memory is called a "memory leak" and results in program code consuming more and more memory over time such that it will eventually crash. (I'm not saying it will do so for our small examples, but freeing memory is part of language learning for non-garbage collecting languages.)
If you're learning a programming language supported by Kattis, there are related problems you can make use of there:
count
counting the number of arrow heights of previously popped balloons: For each balloon height h
read, either count[h + 1] == 0
and you should make a new shot and increment count[h]
, or you should decrement count[h + 1]
and increment count[h]
(simulating this balloon being popped by a previously fired arrow). Finally, print the number of shots needed.