handout #4
CSE341—Programming Languages
ML Programming
Assignment #3
due 11 pm Thursday, 4/22/10
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 CollectIt 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 standard 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 use the “o” operator to compose functions
and 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 f = fn : int
-> int
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, real, 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 acronym(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 acronym = fn : string list ->
string
For example, acronym["what",
"you", "see", "is", "what",
"you", "get"]) should return “WYSIWYG” and acronym(["there",
"ain't", "no", "such",
"thing", "as", "a", "free",
"lunch"]) should return “TANSTAAFL”.
You may assume that the strings are not empty.
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).
Because you
are writing so much code for problem 11, you are allowed to include helper
functions and val
declarations in the top level environment.
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.