CS 550
Spring 2015
Programming Languages
Thursdays 18:30-21:20
Korman Center 104C

Instructor:
Geoffrey Mainland
mainland+cs550@cs.drexel.edu
University Crossings 106
Teaching Assistant:
Mark Boady
mwb33@drexel.edu
Warning! This material is for an old version of the course.

In this assignment, you will implement the Luhn algorithm for validating credit card numbers and solve the Tower of Hanoi problem in Haskell. 1

You must implement the functions as specified. You may write other helper functions and define test data in your file, but you may not change the functions’ names or the number or order of arguments.

Before you attempt the homework, be sure you are using our version of GHC. See the GHC guide.

We have provided you with a shell for your solution here. Please extract this tarball in your ~/cs550/git directory and immediately commit the resulting hw3 directory. You can do this as follows:

$ cd ~/cs550/git
$ wget 'https://www.cs.drexel.edu/~mainland/courses/cs550-201435/homework/hw3.tar.gz'
$ tar xf hw3.tar.gz
$ git add hw3
$ git commit -m "Initial check-in for homework 5."

All your changes should be made to the file HW03.hs. Be sure to commit your work to the repository.

We have included several test for your convenience. Passing all provided tests does not guarantee full credit, but failing tests does guarantee less than full credit. You can run the tests by typing make run-tests in ~/cs550/git/hw3. Code that does not compile will receive a zero. Please run the tests.

This assignment is worth 50 points. There are 51 possible points.

Problem 1: Implementing the Luhn Algorithm (30 points total)

The Luhn algorithm is used to check the validity of credit card numbers. You can read about it on Wikipedia here. For this problem, you will implement the Luhn algorithm in Haskell. The algorithm encompasses the following steps:

  1. Double the value of every second digit beginning from the right. That is, the last digit is unchanged; the second-to-last digit is doubled; the third-to-last digit is unchanged; and so on. For example, [1,3,8,6] becomes [2,3,16,6].
  2. Add the digits of the doubled values and the undoubled digits from the original number. For example, [2,3,16,6] becomes 2+3+1+6+6 = 18.
  3. Calculate the remainder when the sum is divided by 10. For the above example, the remainder would be 8. If the result equals 0, then the number is valid.

Problem 1.1 (5 points)

We first need to be able to break up a number into its last digit and the rest of the number. Write these functions:

lastDigit :: Integer -> Integer
dropLastDigit :: Integer -> Integer

If you’re stumped, look through some of the arithmetic operators mentioned in the lecture.

Example output:

GHCi, version 7.8.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :load HW03.hs
[1 of 1] Compiling HW03             ( HW03.hs, interpreted )
Ok, modules loaded: HW03.
*HW03> lastDigit 123
3
*HW03> lastDigit 0
0
*HW03> dropLastDigit 123
12
*HW03> dropLastDigit 5
0
*HW03>

Problem 1.2 (5 points)

Now, we can break apart a number into its digits. Define the function

toDigits :: Integer -> [Integer]

toDigits should convert positive Integers to a list of digits. (For 0 or negative inputs, toDigits should return the empty list.)

Examples:

toDigits 1234 == [1,2,3,4]
toDigits 0 == []
toDigits (-17) == []

Problem 1.3 (5 points)

Once we have the digits in the proper order, we need to double every other one. Define a function

doubleEveryOther :: [Integer] -> [Integer]

Remember that doubleEveryOther should double every other number beginning from the right, that is, the second-to-last, fourth-to-last, … numbers are doubled. Note that it’s much easier to perform this operation on a list of digits that’s in reverse order. You will likely need helper functions to make this work.

Examples:

doubleEveryOther [8,7,6,5] == [16,7,12,5]
doubleEveryOther [1,2,3] == [1,4,3]

Problem 1.4 (5 points)

The output of doubleEveryOther has a mix of one-digit and two-digit numbers. Define the function

sumDigits :: [Integer] -> Integer

to calculate the sum of all digits.

Example:

sumDigits [16,7,12,5] = 1 + 6 + 7 + 1 + 2 + 5 = 22

Problem 1.5 (10 points)

Define the function

validate :: Integer -> Bool

that indicates whether an Integer could be a valid credit card number. This will use all functions defined in the previous exercises.

Examples:

validate 4012888888881881 = True
validate 4012888888881882 = False

Problem 2: Tower of Hanoi (10 points)

The Tower of Hanoi is a puzzle in which disks of different sizes are stacked on three pegs. The goal is to get from the initial state, in which all disks are stacked on the first peg, to a final state in which all disks are stacked on the second peg. The only rules are:

  1. Only one disk may be moved at a time.
  2. A larger disk can never be stacked on a smaller disk.

The Tower of Hanoi has an elegant recursive solution: to move $n$ disks from peg $A$ to peg $B$,

  1. Move $n-1$ disks from peg $A$ to peg $C$ using peg $B$ as intermediate storage.
  2. Move the $n$th disk from peg $A$ to peg $B$,
  3. Move $n-1$ disks from peg $C$ to peg $B$ using peg $A$ as intermediate storage.

You will define a function hanoi that when given an integer and the names of three pegs, outputs a list of moves necessary to transfer the stack of disks from the first peg to the second.

type Peg = String
type Move = (Peg, Peg)

hanoi :: Integer -> Peg -> Peg -> Peg -> [Move]

Example output in ghci:

> hanoi 2 "a" "b" "c"
[("a","c"), ("a","b"), ("c","b")]

See Wikipedia for a full explanation of the Tower of Hanoi game.

Hints: My solution is two lines: base case and inductive case. You will probably want to use list functions like ++. Read the LYAH Intro to Lists if you haven’t already.

Problem 3: Sets (10 points)

Implement the power set function, $\mathcal{P}(\cdot)$, from lecture. Recall that the power set $\mathcal{P}(S)$ of a set $S$ is the set of all subsets of $S$, including the empty set and $S$ itself.

The type signature for powerSet should look like this:

powerSet :: ... => ... -> ...

Before you write the function, figure out what its type signature should be. If you cannot write the function, you will receive partial credit for a correct type signature. However, your code must compile for you to receive any credit, so if you only write a type signature for powerSet, leave it in a comment.

Please note the following constraints:

  1. You may not change the code in Set.hs.
  2. Solutions that use fromList or toList will receive zero credit. You must use the Set abstraction without relying on the fact that “under the covers” it is implemented using lists.

Problem 4: Homework Statistics (1 point)

How long did it take you to complete each problem? Please tell us in a comment at the beginning of the file HW03.hs. To receive the point, tell us, for each individual problem, how long the problem took you. If you tell us how long the assignment took you as a whole, you will not receive the point.

Notes

  1. These problems are adapted from Brent Yorgey’s course, “Introduction to Haskell”.