handout #8
CS341—Programming Languages
ML Programming
Assignment #3
due 11 pm Wednesday, 1/24/07
In writing this
problem set you will need a set of utility functions defined in a file called
utility.sml available from the class web page.
You will also need a file called personality.sml that has supporting
data and functions for problem 11. The
file personality.sml includes a call on the use function for utility.sml, so
you can simply have a call on use for personality.sml to get everything. Include your answers in a file called hw3.sml
and submit it through the catalyst e-submit area that is linked from the class
web page.
Problems 1-4 must be
solved as one-line functions involving map, filter and reduce and the infix
operator -- (all included in utility.sml).
You are not allowed to use if/else expressions, let constructors or
pattern matching. You should declare all
helper functions as anonymous functions.
You may use the standard functions length and real and all of the
standard operators (+, -, *, /, div, mod, @, ::, <,
>, <=, >=, =, <>). You
may also use the “op” keyword to convert a standard operator into a function.
1.
(3 points) Define a function multiples(m,
n) that takes two integers as arguments and that returns a list of the first m
multiples of n:
val multiples = fn : int * int -> int list
For example, multiples(3, 7) should return [7,14,21]. You may assume that m is greater than or
equal to 0.
2.
(3 points) Define the sumTo(n) function
of assignment 1 that takes an integer as an argument and that returns the sum
of the first n reciprocals:
val sumTo = fn : int -> real
For example, sumTo(2) should return 1.5.
You may assume that n is greater than 0 (handling the 0 case would
require an if/else or a better version of one of these functions).
3.
(3 points) Define the numNegative(lst)
function of assignment 1 that takes a list of integers as an argument and that
returns the number of negative values in the list:
val numNegative = fn : int list -> int
For example, numNegative([3, 17, ~9, 34, ~7, 2]) should return 2.
4.
(6 points) Define a function allPairs(m,
n) that takes two integers as arguments and that returns a list of all tuples
whose first value is between 1 and m and whose second value is between 1 and n:
val allPairs = fn : int * int -> (int * int)list
The tuples should
appear in nondecreasing order using the first value and within those groups, in
increasing order by the second value.
For example, allPairs(3, 4) should return [(1,1),(1,2),(1,3),(1,4),(2,1),(2,2),(2,3),(2,4),(3,1),(3,2),(3,3),(3,4)]. You may assume that each of m and n are
greater than 0.
Problems 5—10 must be
solved as one-line val declarations using curried
functions and function composition. This
will allow you to practice the writing of partially instantiated
functions. You are not allowed to define
these functions or any helper functions directly using the “fun” declaration or
using the anonymous function notation (“fn”).
You have to instead define them with val declarations in terms of
existing functions, standard operators (see the list above) and the functions
included in utility.sml (curry, map2, filter2, reduce2, and the infix operator
--). Remember that you can apply the curry function to infix operators like +,
-, /, div, mod, --, <, >, etc. as in:
val double = curry op* 2;
5.
(2 points) Define a function f(n) that takes an integer
argument and that returns 2n + 3:
val positive = fn : int -> bool
6.
(2 points) Define a function positive(n) that takes an
integer arument and that returns true if n is positive (strictly greater than
0) and that returns false otherwise:
val positive = fn : int -> bool
7.
(4 points) Define a function evens(n) that takes an
integer argument and that returns a list of the first n even numbers (2, 4, 6,
8, etc):
val evens = fn : int -> int list
For example, the call evens(8) should return [2,4,6,8,10,12,14,16]. You may assume that n is not negative.
8.
(4 points) Using ML’s built-in round and Math.sqrt
functions, define a function roots(lst) that takes a list of integers as a
parameter and that returns a list of the square roots of the positive integers
in the list rounded to the nearest integer:
val roots = fn : int list -> int
list
Your function should
ignore integers that aren’t positive (i.e., should exclude them from the
result). For example, roots([1,
3, ~8, 14, 0, 7, ~22, 45]) should return [1,2,4,3,7]. You may use your positive function from
problem 6 to solve this problem.
9.
(4 points) Using ML’s Char.toUpper function that returns
the uppercase version of a character and the built-in functions implode,
explode and hd, define a function firsts(lst) that takes a list of strings as
an argument and that returns a string composed of the uppercased first letters
of each string in the list:
val firsts = fn : string list ->
string
For example, firsts(["four", "score",
"and", "seven", "years", "ago"]) should
return “FSASYA”.
10. (4
points) Using ML’s built in function rev
that reverses a list and the built-in functions implode and explode, define a
function reverseStrings(lst) that takes a list of strings as an argument and
that returns the list obtained by reversing the order of the letters in each
word and reversing the overall order of the words:
val reverseStrings = fn : string list
-> string list
For example, reverseStrings(["four",
"score", "and", "seven", "years",
"ago"]) should return ["oga","sraey","neves","dna","erocs","ruof"].
The remainder of your
assignment involves working with a personality test that is described in detail
below.
11. (65
points) Use a val
declaration to define a variable answer that contains a list of triples that
uses the variable data defined in personality.sml to produce a a solution to
the personality test, as described below.
Obviously you will write many helper functions to be able to solve this
task. You can make use of any of the
functions in utility.sml and personality.sml.
For this particular problem you are also allowed to use the full range
of ML features, including everything we have studied so far and also including
functions from the ML library (e.g., Math, Int, String, Real, List). Each triple in your list should contain
information for one person from the variable called data defined in
personality.sml. The triple should have
the person’s name, a list of their four personality scores and their
personality type. Sample output is
available from the class web page. Your
list must be sorted by personality type and subsorted by name (i.e.,
alphabetical within a given personality type).
Your variable should be of the following type:
(string *
int list * string) list
You can display the
result in a nicer format by calling displayAll(answer). The rest of this writeup describes the
details of the personality test.
The bulk of this
assignment involves developing code that evaluates data obtained from the
Keirsey personality test. The data will
be in the form of a list of entries, one per person, where each entry is a
tuple with a name (of type string) followed by a string of seventy characters
(A’s, B’s, and dashes).
The Keirsey test
measures four independent dimensions of personality:
·
Extrovert versus Introvert (E vs I): what
energizes you
·
Sensation versus iNtuition (S vs N): what
you focus on
·
Thinking versus Feeling (T vs F): how you
interpret what you focus on
·
Judging versus Perceiving (J vs P): how
you approach life
Individuals are
categorized as being on one side or the other of each of these dimensions. The corresponding letters are put together to
form a personality type. For example, if
you are an extravert, intuitive, thinking, perceiving person then you are referred to as an ENTP.
Usually the letter is the first letter of the word, but notice that
because the letter “I” is used for “Introvert”, the letter “N” is used for
“iNtuition.”
Remember that the
Keirsey test involves 70 questions answered either A, B or – (no answer). You
should compute a score in each of the four dimensions by computing the percent
of B responses for that dimension. Some
of the characters will be dashes to indicate that the user did not answer the
question. These answers should not be
included in the percent computation. The
percentages should be rounded to the nearest integer.
These numbers are then
converted into a string of letters. The A answers correspond to extravert,
sensation, thinking and judging (E, S, T and J). The B answers correspond to introvert,
intuition, feeling and perceiving (I, N, F and P). If a person is closer to the A side, you use
that letter. If they are closer to the B
side, you use that letter. If they are
exactly in the middle, you use the letter “X”.
You will be provided a
file called personality.sml that contains useful definitions for this
assignment. It defines a function
translate1 that takes a string of 70 A's, B's and dashes and that returns a
list of 4 lists of strings, one for each dimension of the personality
test. The fact that this function is
called translate1 is a hint that perhaps you want to define your own functions
(translate2, translate3, etc) that perform other transformations on the data. You also have access to all of the utility
functions (qsort, map, filter, reduce) and you are encouraged to use them. You may also use the curried versions of
these functions if you prefer (map2, filter2, reduce2).
Your program should
not depend on the specific number 70, although we are assuming that the answers
always appear in a particular pattern that is repeated every 7 characters and
we are assuming that the overall length is a multiple of 7. You also will be writing code that is
specific to this personality test and its four dimensions. You may also assume that it will be possible
to compute a percentage for each dimension (i.e., that no user has all blank
answers for any of the four dimensions).
Extra Credit
Another way to look at the personality data is
to think of it as coordinates in 4-space.
We can use these coordinates to compute the distance between
individuals. For our purposes, we will
round the 4-dimensional distance to the nearest integer.
12. (4
points) Define a variable bigAnswer that is a list of triples of the following
type:
(string *
int * (string * int) list) list
The first element
should be a person’s name, the second element should be their average distance
from other people (this can be the truncated average of the rounded integers),
and the third element should be a list of distance tuples that have a person’s
name and their distance from the given individual. A person should not appear in their own list
of distance tuples. The overall list
should be sorted alphabetically by name.
The individual distance tuple lists should be sorted by increasing
distance from the individual. A sample
output will be available from the class web page.