GIML: Election Special

British Election Results may be obtained from ftp://ftp.demon.co.uk/pub/British_Politics
Local copies at /usr/local/etc/ftp/pub/a.cumming/election This directory include parse.sml and eg.sml which contain function to read and to examine the data.

Model answers to some coursework questions.

parse.sml

The file parse.sml contains several useful functions for reading and processing the election data. Because the data is slightly inconsistent in places it is rather more complex than we would prefer.
Here are a few of the functions:

Example

We consider the example given in eg.sml

Question

Do all the percentages of all the candidates for all the constituencies fror all the years add to 100?
use "/users/staff/sez125/election/parse.sml";
val allFiles = map (fn s=>"/users/staff/sez125/election/1983."
                                  ^s^".txt") ["01","02","03","04","05",
         "06","07","08","09","10", "11","12","13","14","15", "16","17"];
We load in the utilities in parse.sml and define a list of all file names, each file contains roughly 50K giving around 1000K in total. This should not be a problem for the HP's.
We obtain a list of all constituencies - call them r:
val r = flatten(map read allFiles);
Now extract the list of years for each constituency - this means ditching the constituency name
map snd r;
Now flatten to obtain a list of all candidates
flatten it;
The function (map perMil) will extract only the percentage for each set of results, we want to map this over the whole list, in fact we get 10 times the percentage as the decimal point is ignored
map (map perMil) it;
We now sum each of the lists
map sum it;
Most of these are 1000 - remove them
filter (not o member [1000]) it;
999's and 1001's may be due to rounding errors
filter (not o member [999,1001]) it;
We are left with three rogue results - a little more work is required to identify them
fun check t = filter (not o (forallP (member [1000,1001,999])) o snd)
                (map (appsnd
                (map (sum o (map perMil)))) t);
check r;

Question

Which Labour candidate obtained the fewest votes in the 1992 election?
Throw away the constituency name and structure
flatten (map snd r);
Extract the 1992 results
filter (is_yearP "1992") it;
Extract the Labour candidates from the flattened list
filter (partyP "Lab") (flatten it);
sort according to size of vote
sort (fn(a,b)=>votes a > votes b) it;
We want to look at the end of this list
rev it;
Dr. M. Lynda Clarke wins the wooden spoon.

Bug note

There is a "feature" of the Windows version of ml which prevents the can_input function from returning anything until a read has been attempted. We can work around this by replacing open_input with a new version. Simply replace the given definition of read in parse.sml with ...
fun open_in' f = let val tmp = open_in f val w = lookahead tmp
        in tmp end;
fun read f = fst(con_file(clean(readall(open_in' f))));

Predicates

A predicate is for our purposes a function which returns true or false. You may think of a predicate as a question - for this reason predicates are given names which end in capital P (which looks like a question mark).
Examples
fun evenP x = (x mod 2 = 0);
This function returns true if the input is even and false otherwise.
fun palindromeP s = (explode s = rev(explode s));
This returns true for palindromic strings.

Combining predicates

When can use functions such as eitherP and forallP to construct others.
evenP o size is the predicate which asks "does this string contain an even number of characters?" and so the predicate eitherP (evenP o size) palindromeP asks "is this string either of even length or a palindrome?" (Naturally we mean an inclusive or here).
Answer the following:
eitherP (evenP o size) palindromeP "tat";
eitherP (evenP o size) palindromeP "bat";
eitherP (evenP o size) palindromeP "shut";

forallP (eitherP (evenP o size) palindromeP) ["shut","madamimadam","abba"];