Joseph Haugh
University of New Mexico
By the end of this lesson, you will know how to:
zip functionStringscaesar 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:
ghci> 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)
ghci> pairs [1, 2, 3, 4]
| { applying pairs }
zip [1, 2, 3, 4] (tail [1, 2, 3, 4])
| { applying tail }
zip [1, 2, 3, 4] [2, 3, 4]
| { applying zip }
[(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.
ghci> zip [1, 2, 3] [4, 5]
[(1, 4), (2, 5)]
ghci> 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 Strings
count :: Char -> String -> Int
count c s = length [ x | x <- s, x == c ]
ghci> 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 Chars
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 CharFirst let’s implement two functions to convert between Chars 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)
ghci> let2int 'a'
0
ghci> 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.
ghci> shift 3 'a'
'd'
ghci> shift 3 'z'
'c'
ghci> shift (-3) 'c'
'z'
ghci> 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 ]
ghci> encode 3 "haskell"
"kdvnhoo"
ghci> 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:
ghci> sumSquares 5
| { applying sumSquares }
sum [ 1^2, 2^2, 3^2, 4^2, 5^2 ]
| { applying (^)s and sum }
55
sumSquares :: Int -> Int
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:
ghci> sumSquares 5
| { applying sumSquares }
sum [ 1^2, 2^2, 3^2, 4^2, 5^2 ]
| { applying (^)s and sum }
55
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 xs.
Note the ' in the name to avoid conflicting with the built-in definition.
For example:
ghci> replicate' 3 5
[5, 5, 5]
replicate' :: Int -> a -> [a]
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 xs.
Note the ' in the name to avoid conflicting with the built-in definition.
For example:
ghci> replicate' 3 5
[5, 5, 5]
replicate' :: Int -> a -> [a]
replicate' n x = [ x | _ <- [1..n] ]