Conference: Week 3

Table Of Contents

ML!

1. List Reverse

Here is some start code for the problems this week: week-3-starter.sml

Without using any built-in SML functions, write an SML function

rev : 'a list -> 'a list

that takes a list l as an argument and returns a list of the elements of l in the reversed order. Once you have implemented the function, you should get the following behavior at the SML prompt:

- rev [1,2,3,4];
val it = [4,3,2,1] : int list
- rev ["a","b","c","d"];
val it = ["d","c","b","a"] : string list
Solution
fun rev nil = nil
  | rev (x::xs) = rev(xs) @ [x];

2. Palindromes

A palindrome is a word that reads the same from left-to-right and right-to-left. Write an SML function

isPalindrome : string -> bool

that takes a string w and returns true if the string is a palindrome and false otherwise. You may want to use the SML function explode, which takes a string and returns a char list containing the individual characters of the string. Once you have implemented the function, you should get the following behavior at the SML prompt:

- isPalindrome "malayalam";
val it = true : bool
- isPalindrome "myxomatosis";
val it = false : bool
Solution
fun isPalindrome(m) =
  let val e = explode(m) in
    rev(e) = e
  end;
Solution

3. Digital Roots and Additive Persistence

Write an SML function

digits : int -> int list

that takes a non-negative integer \(n\) as an argument and returns the list of digits of \(n\) in the order in which they appear in \(n\):

- digits 3124;
val it = [3,1,2,4] : int list
- digits 352663;
val it = [3,5,2,6,6,3] : int list

Now write an SML function

listAdd : int list -> int

that takes an integer list \(l\) and returns the sum of the elements of \(l\):

- listAdd [1,2,3,4];
val it = 10 : int
- listAdd [1,~2,3,5];
val it = 7 : int

Consider the process of taking a number, adding its digits, then adding the digits of the number derived from it, etc., until the remaining number has only one digit. The number of additions required to obtain a single digit from a number \(n\) is called the additive persistence of \(n\), and the digit obtained is called the digital root of \(n\). For example, the sequence obtained from the starting number \(9876\) is \(9876 \rightarrow 30 \rightarrow 3\), so \(9876\) has an additive persistence of \(2\) and a digital root of \(3\). Write two SML functions

additivePersistence : int -> int
digitalRoot : int -> int

that take integer arguments \(n\) and return respectively the additive persistence and the digital root of \(n\):

- additivePersistence 9876;
val it = 2 : int
- digitalRoot 9876;
val it = 3 : int

There are two ways to do this. Write two completely separate functions, or since they will be very similar, write one function that computes both the additivePersistance and digitalRoot and returns those two values as a tuple. Then additivePersistance and digitalRoot are one-line functions that just call the helper and extract the appropriate value.

Solution
fun digits n =
  if n < 10 then [n]
  else (digits (n div 10)) @ [n mod 10];

fun listAdd nil = 0
  | listAdd (x::xs) = listAdd(xs) + x;

fun persistanceAndRoots n =
  let val sum = listAdd(digits n) in
    if (sum < 10) then (1, sum)
    else 
      let val (ap, root) = persistanceAndRoots(sum) in
        (ap + 1, root)
      end
  end;

fun additivePersistence n = #1(persistanceAndRoots(n));
fun digitalRoot n = #2(persistanceAndRoots(n));

4. List Cleaning

Without using any built-in SML functions, write a function

clean: ''a list -> ''a list

that takes a list l and returns the list of elements of l with the duplicates, i.e. second, third, etc. occurrences, removed, and where the remaining elements appear in the same order as in l . Once you have implemented the function, you should get the following behavior at the SML prompt:

- clean [1,6,2,4,12,2,13,6,9];
val it = [1,6,2,4,12,13,9] : int list

You’ll probably want to use our contains function from lecture:

fun contains x nil = false
  | contains x (y::ys) = x = y orelse contains x ys;
Solution
fun contains x nil = false
  | contains x (y::ys) = x = y orelse contains x ys;
        
fun cleanList list = 
  (* keep *first* occurence of each element in cleaned *)
  let fun helper cleaned nil      = cleaned
        | helper cleaned (x::xs)  = 
            if (contains x cleaned) then helper xs cleaned
                                    else x :: (helper xs x::cleaned)
  in 
    helper nil list
  end;

Alternative that’s less efficient but more expedient to write:

fun contains x nil = false
  | contains x (y::ys) = x = y orelse contains x ys;
        
fun cleanList list = 
  (* keep *last* occurence of each element *)
  let fun helper nil      = nil
        | helper (x::xs)  = if (contains x xs) then helper xs 
                                               else x::(helper xs)
  in 
    rev(helper(rev(list)))
  end;