CS 360
Winter 2018
Programming Language Concepts
CS 360-001 Tuesday/Thursday 15:30-16:50 (Rush 209)
CS 360-002 Tuesday/Thursday 14:00-15:20 (Rush 209)
CS 360-003 Tuesday 18:30-21:20 (UCross 151)
Instructor: |
Geoffrey Mainland mainland@drexel.edu Office: University Crossings 106 Office hours: Mondays 4pm–7pm; Thursdays 5pm–6pm. |
Teaching Assistant: |
Xiao Han CLC office hours: Tuesday 12pm–2pm; Thursday 6pm–8pm Allen Yang CLC office hours: Wednesday 6pm–8pm |
Due Monday, February 19, 11:59:59PM EST.
Accept this assignment on GitHub Classroom here. The standard homework instructions apply.
In this assignment, you will implement the Luhn algorithm for validating credit card numbers and solve some additional small problems 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.
You should complete the homework by modifying the file src/HW04.hs
that we provide as part of your repository.
Make sure you are using the correct version of GHC, 8.0.2. If you are using the course VM, you are all set. If you are using tux
, please read our instructions for getting the course software on tux
to make sure you have the correct version.
Note: The first time you build your code, it will take some time, as all prerequisites will need to be automatically downloaded and installed. Subsequent builds will be much faster.
This assignment is worth 50 points. There are 51 possible points.
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,3,8,6]
becomes [2,3,16,6]
.[2,3,16,6]
becomes 2+3+1+6+6 = 18
.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 8.0.2: http://www.haskell.org/ghc/ :? for help
Prelude> :load HW04.hs
[1 of 2] Compiling Set ( Set.hs, interpreted )
[2 of 2] Compiling HW04 ( HW04.hs, interpreted )
Ok, modules loaded: HW04, Set.
*HW04> lastDigit 123
3
*HW04> lastDigit 0
0
*HW04> dropLastDigit 123
12
*HW04> dropLastDigit 5
0
*HW04>
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) == []
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]
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
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
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:
The Tower of Hanoi has an elegant recursive solution: to move $n$ disks from peg $A$ to peg $B$,
You will define a function hanoi
that when given an integer $n$ and the names of three pegs, outputs a list of moves necessary to transfer the stack of $n$ 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.
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. Also recall the inductive proof that a set with $n$ elements has a power set with $2^n$ elements.
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
, don’t remove the dummy function definition.
One of the goals of this problem is to treat Set
as an abstract data type; we should be able to change the representation of sets, keeping the external interface the same, and the code you wrote to compute the power set would run unchanged. This means that you cannot rely in any way on the fact that sets happen to be implemented as sorted lists with no duplicates. How then can you test for an empty set? Fortunately, there is an isEmpty
predicate exported by the Set
module. How can you split a set into an element plus the rest of the set? Again, we fortunately have a split
function exported by the Set
module.
If you are stuck trying to pattern match on the structure of a set, stop! You can’t do that, since you don’t get to see the representation! Instead, pattern match and use guards, or, if you prefer, just use Haskell’s if
.
Please note the following constraints:
Set.hs
.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.Set
module, even if it uses fromList
or toList
internally. Think of the Set
module as a black box whose implementation you are not allowed to see.How long did spend on each problem? Please tell us in your README.md
. You may enter time using any format described here.
These problems are adapted from Brent Yorgey’s course, “Introduction to Haskell”. ↩