~cs538-1/public/handin/proj2/login
where login
is your login name.1,
2a, 2b, 2c, 3a, 3b, 4a, 4b, 4c
. You can also include a file
called README
with any notes about your submission,
including the timing results for part 3b. PLEASE NOTE: name
your files 1, 2a, 2b ...
without an extension and not 1.sml,
2a.sml ...
or anything else. Put all the files in the handin
directory and do not make separate subdirectories for each question. 2a
in your
solution for 2b
), and don't assume
that your code will execute in the handin folder. To achieve this, you
may
need to make copies of code that is common to multiple sub-parts of the
same
question. E.g., copy the definition of unit-lists
from file 2a
into file 2b
. Also make sure to define your types, functions, values,
exceptions to be EXACTLY the same type as shown here, and they
should behave in EXACTLY the same way as shown below. Functions
should be of the right type and take arguments in the right order,
e.g., function enter
in question 1 should take a single
tuple of 3 elements (priority, value, queue in that order) as its
single argument rather than 3 separate arguments. Furthermore,
'priority' should be an integer, 'value' can be any type T, and 'queue'
should be of type T PriorityQ (matching the type T of 'value')
If you are unclear about the types or format or purpose of
function arguments and return values, please clarify the matter with
the instructor or TA before submitting the code.
Question | Required definitions | Sample usage |
---|---|---|
1 | type 'a PriorityQ | |
2a | val unitLists = fn: int list -> int
list list |
- unitLists [3,1,10,7,6]; |
2b | val mergeSort = fn: int list ->
int list |
- mergeSort [3,1,10,7,6]; |
2c | val mergeSort2 = fn: int list ->
int list option |
- mergeSort2 [3,2,2,1]; |
3a | val f = fn: int -> int |
- f 5; |
3b | val fastF = fn: int -> int
|
- fastF 5; |
4a | datatype 'a lazyList = cons of 'a *
(unit -> 'a lazyList) | nullList |
- val s1 = seq(10,400); |
4b | datatype 'a lazyList = cons of 'a *
(unit -> 'a lazyList) | nullList
|
- val c = cons(false, fn () =>
cons(true, fn () => nullList)); |
4c | datatype 'a lazyList = cons of 'a *
(unit -> 'a lazyList) | nullList |
- val p = primes(); |
sml
.
You are free to work on your own with a newer version of SML which you
might
have downloaded and installed at home but before submission,
you must make sure your code runs with our version of SML. If you use
SML library functions, please make sure that these library functions
are also available in the SML/NJ installed here in CS. print
or other SML
output functions. Each of the functions listed above should silently
return values as required by the question. Also,
please make sure that your files are free of any top-level tests or
timings
that you may have used during testing and debugging. This extra code
can
confuse the automatic tester we use.No extra credit for the most elegant programs but you are encouraged
to write
in a functional style, making best use of SML's natural abstractions:
lists, pattern-matching, recursion, higher-order functions, and
so on.
These will invariably reward you with compact, natural-looking code
which
is easy to read and understand. See the lecture notes and look online
for
SML programs to get a flavor of ML programming.
We may well penalize programs that are excessively kludgy and hard to read
or
understand. Don't try to write C++ or Java in SML. In particular, the
excessive use of the reference operators (ref
and !
and !=
and their cousins) is frowned upon and will be
penalized. Note that this does not apply when an imperative
update using references might be required or natural, as with
memoization in question 3(b).
Partial credit will be given for programs that seem to be on the right
track.
Also see the SML/NJ home page for latest information on the language and available libraries (including the Standard Basis library).
SML/NJ has strong static typing, forcing you to make your program type-correct before it can be run (leading to a slogan of ML fans: "if it type-checks, it just runs"). But SML's error messages can be very cryptic leading to wasted time trying to figure out what SML is trying to tell you. Style note: it is better to use SML's type inference system and not put explicit type annotations everywhere.
When in doubt, parenthesize. It's a common error to omit
parentheses around parameters in patterns, e.g., a list pattern should
be written as (h::t)
and not just h::t
.
Also, each definition requires you to use the val
or fun
keywords, even definitions within a let
clause.
When pattern matching, make sure you cover all combinations of possibilities for arguments. You may not care about all combinations, and some of them might be impossible, but the compiler does not know that. It will warn you of non-exhaustive matches if your pattern-matching clauses are not sufficient.
Also, it is preferable to use pattern matching rather than tuple selectors like #1 or #2, or list selectors like hd or tl. In some cases, using tuple selectors might cause type errors (the inscrutable unresolved flex record) since there is not sufficient information for SML to deduce the exact number of elements in a tuple. In such cases, you should explicitly specify the size of the tuple in the pattern, e.g., write
fun f (a,b) = brather than
fun f t = #2(t)
See this page for some help with deciphering SML error messages, and see the SML/NJ errors list for the definitive reference.