Joseph Haugh
University of New Mexico
By the end of this lesson, you will know how to:
zip
functionString
scaesar
cipherWhat does the following mathematical expression mean?
{ x | x ∈ {1..5} }
The set of all x
such that x
is an element of the set {1, 2, 3, 4, 5}
.
We can do this same thing in Haskell using list comprehensions:
[ x | x <- [1..5] ]
The list of all x
such that x
is an element of the list [1, 2, 3, 4, 5]
.
What does this produce?
[ x ^ 2 | x <- [1..5] ]
The list of all x^2
such that x
is an element of the list [1, 2, 3, 4, 5]
.
[1, 4, 9, 16, 25]
What does this produce?
[ (x, x) | x <- [1..5] ]
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
You can also use multiple generators in a list comprehension:
[ (x, y) | x <- [1, 2, 3], y <- [4, 5] ]
[(1, 4), (1, 5), (2, 4), (2, 5), (3, 4), (3, 5)]
This is called a Cartesian Product
What happens if we flip the order of the generators? Note that the names of the lists and the order of the tuple remain unchanged.
[ (x, y) | y <- [4, 5], x <- [1, 2, 3] ]
[(1, 4), (2, 4), (3, 4), (1, 5), (2, 5), (3, 5)]
You can also use the value of a previous generator in a later generator:
[ (x, y) | x <- [1..3], y <- [x..3] ]
[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
How could you take in a list of tuples and return a list of the second elements of each tuple?
What would the type of that function be?
seconds :: [(Int, Int)] -> [Int]
Can we be more general?
seconds :: [(a, a)] -> [a]
seconds :: [(a, a)] -> [a]
seconds xs = [ y | (_, y) <- xs ]
How could you take in a list and returns its length
using a list comprehension and the sum
function?
What should the type be?
length :: [a] -> Int
length :: [a] -> Int
length xs = sum [ 1 | _ <- xs ]
What about if we only want to take in a list and only return the even numbers squared?
Here is how we can implement that function:
evensSquared :: [Int] -> [Int]
evensSquared xs = [ x ^ 2 | x <- xs, even x ]
How could you take in a number and return a list of its factors?
factors :: Int -> [Int]
factors n = [ x | x <- [1..n], n `mod` x == 0 ]
Using factors
, how could you implement a function to check if a number is prime?
prime :: Int -> Bool
prime n = factors n == [1, n]
The zip function takes two lists and returns a list of tuples where the first element of each tuple is from the first list and the second element is from the second list.
For example:
> zip [1, 2, 3] [4, 5, 6]
[(1, 4), (2, 5), (3, 6)]
A useful technique is to use the zip
function to create a list of pairs
of adjacent elements from a list.
pairs :: [a] -> [(a, a)]
pairs xs = zip xs (tail xs)
> pairs [1, 2, 3, 4]
| zip [1, 2, 3, 4] (tail [1, 2, 3, 4])
| zip [1, 2, 3, 4] [2, 3, 4]
| [(1, 2), (2, 3), (3, 4)]
What happens when the lists given to zip
are of different lengths?
As you can glean from the previous example, zip
stops when the shorter list ends.
> zip [1, 2, 3] [4, 5]
[(1, 4), (2, 5)]
> zip [1, 2] [4, 5, 6]
[(1, 4), (2, 5)]
Recall that a String
in Haskell is [Char]
This means that we can use list comprehensions to manipulate String
s
count :: Char -> String -> Int
count c s = length [ x | x <- s, x == c ]
> count 's' "mississippi"
4
Julius Caesar would encode his messages by shifting each letter by 3 to the right, and wrapping around to the beginning if necessary.
We will need to import a library to manipulate Char
s
in order to implement the caesar cipher
import Data.Char
This library includes helpful functions like ord
and chr
ord
takes a Char
and returns its ASCII valuechr
takes an ASCII value and returns the corresponding Char
First let’s implement two functions to convert between Char
s and their ASCII values,
but make it so that lowercase ‘a’ is 0, ‘b’ is 1, and so on.
let2int :: Char -> Int
let2int c = ord c - ord 'a'
int2let :: Int -> Char
int2let n = chr (ord 'a' + n)
> let2int 'a'
0
> int2let 0
'a'
Now we can implement a function to shift a lower case letter by n
places
to the left or right
shift :: Int -> Char -> Char
shift n c
| isLower c = int2let ((let2int c + n) `mod` 26)
| otherwise = c
Note this only works on lower case letters.
> shift 3 'a'
'd'
> shift 3 'z'
'c'
> shift (-3) 'c'
'z'
> shift 3 'A'
'A'
Now we can easily define a function which encodes a given String
using the caesar cipher method
encode :: Int -> String -> String
encode n xs = [ shift n x | x <- xs ]
> encode 3 "haskell"
"kdvnhoo"
> enocde (-3) "kdvnhoo"
"haskell"
Use a list comprehension to implement a function which calculates the sum of the first n
squares
Recall that the sum
function can be used to sum a list of numbers.
For example:
> sumSquares 5
| sum [ 1^2, 2^2, 3^2, 4^2, 5^2 ]
| 55
sumSquares :: Int -> Int
sumSquares :: Int -> Int
sumSquares n = sum [ x ^ 2 | x <- [1..n] ]
Use a list comprehension to implement the replicate'
function, this
function is given an Int
n
and a value x
and returns a list of n
x
s.
Note the '
in the name to avoid conflicting with the built-in definition.
For example:
> replicate' 3 5
[5, 5, 5]
replicate' :: Int -> a -> [a]
replicate' :: Int -> a -> [a]
replicate' n x = [ x | _ <- [1..n] ]