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.