parent
f882643891
commit
05428e0799
28 changed files with 820 additions and 0 deletions
@ -0,0 +1,6 @@ |
||||
# Chapter Exercises |
||||
## Warm-up and review |
||||
see src/warmup.hs |
||||
|
||||
## Rewriting functions using folds |
||||
see src/rewrite.hs |
@ -0,0 +1,40 @@ |
||||
# Exercises: Understanding folds |
||||
## Exercise 1 |
||||
This will evaluate to: `(1*(2*(3*(4*(5*1))))`, so |
||||
1. Is not correct as the types are incorrect. |
||||
2. Will return the same result. |
||||
3. Will also return the same result. |
||||
The latter two are the same because multiplication is commutative. |
||||
|
||||
## Exercise 2 |
||||
``` |
||||
fMul = flip (*) |
||||
| ------- accumulator -------| |
||||
foldl fMul 1 [1,2,3] |
||||
= foldl fMUl (fMul 1 1) [2,3] |
||||
= foldl fMul (fMul (fMul 1 1) 2) [3] |
||||
= foldl fMul (fMul (fMul (fMul 1 1) 2) 3) [] |
||||
= fMul (fMul (fMul 1 1) 2) 3 |
||||
= 3*(2*(1*1)) |
||||
= 6 |
||||
``` |
||||
|
||||
## Exercise 3 |
||||
1. They both traverse the spine from right to left. This evident by the `(x:xs)` pattern matching in both functions' definition. |
||||
2. They both force the rest of the fold as they are both recursive functions. |
||||
3. **True, `foldr` associates from the right and `foldl` assocatiates from the left.** |
||||
4. They are both recursive |
||||
|
||||
## Exercise 4 |
||||
(a) Catamorphism means to shape downwards and thus reduce structure. This is evident in the application of the folds which can reduce lists to a single value. |
||||
|
||||
## Exercise 5 |
||||
1. The zero value is missing: `foldr (++) "" ["woot", "WOOT", "woot"]` |
||||
2. The wrong zero value is used, a `Char` is expected: `foldr max 'a' "fear is the litle death"` |
||||
3. `and :: [a] -> Bool` is a function which takes an array and returns `True` if all values are `True`. This is not the same type as expected: `(a -> b -> b)`. **Should be: `foldr (&&) True [False, True]`** |
||||
4. The wrong zero value is used as this will always return `True`. Such is the nature of the `(||)`operator. Should be: `foldr (||) False [False, True]` |
||||
5. `foldl` expects a `b -> a -> b` function. `((++) . show) :: Show a => a -> [Char] -> [Char]`. If we use this function this means that, because the accumalator starting values `""` is of type `[Char]`, our fold function is: `[Char] -> [Char] -> [Char]` which does not fit, because this would mean our elements of the array should be of type `[Char]`. **It should be: `foldr (flip $ ((++) . show) "" [1..5]`** |
||||
6. The result of the `const` application should be the same type as the zero value (`Char`), but `const` returns the type of the second argument, in this case `Num a => a`. **It should be: `foldr (flip const) 'a' [1..5]`** |
||||
7. Same as (6). It should be: `foldr (flip const) 0 "tacos"` |
||||
8. This is similar to the previous questions. The `flip` is not needed here for the same reasons it is needed in (6) and (7). It should be: `foldl const 0 "burritos"` |
||||
9. Same as (8). It should be `foldl const 'z' [1..5]` |
@ -0,0 +1,2 @@ |
||||
# Exercises: Database Processing |
||||
see src/dbprocess.hs |
@ -0,0 +1,2 @@ |
||||
# Scan Exercises |
||||
see src/fibs.hs |
@ -0,0 +1,63 @@ |
||||
module DBProcess where |
||||
|
||||
import Data.Time |
||||
|
||||
data DatabaseItem = DbString String |
||||
| DbNumber Integer |
||||
| DbDate UTCTime |
||||
deriving (Eq, Ord, Show) |
||||
|
||||
theDatabase :: [DatabaseItem] |
||||
theDatabase = |
||||
[ DbDate (UTCTime |
||||
(fromGregorian 1911 5 1) |
||||
(secondsToDiffTime 34123)) |
||||
, DbNumber 9001 |
||||
, DbString "Hello, world!" |
||||
, DbDate (UTCTime |
||||
(fromGregorian 1921 5 1) |
||||
(secondsToDiffTime 34123)) |
||||
] |
||||
|
||||
-- 1 |
||||
filterDbDate :: [DatabaseItem] -> [UTCTime] |
||||
filterDbDate = foldr f [] |
||||
where f (DbDate u) xs = u : xs |
||||
f _ xs = xs |
||||
|
||||
-- 2 |
||||
filterDbNumber :: [DatabaseItem] -> [Integer] |
||||
filterDbNumber = foldr f [] |
||||
where f (DbNumber i) xs = i : xs |
||||
f _ xs = xs |
||||
|
||||
-- 3 |
||||
mostRecent :: [DatabaseItem] -> UTCTime |
||||
mostRecent = maximum . filterDbDate |
||||
|
||||
-- with folds, this is more difficult, because it's not easy to find the |
||||
-- right zero value. Ideally it would be the smallest UTCTime possible. |
||||
mostRecent' :: [DatabaseItem] -> UTCTime |
||||
mostRecent' = foldr f z |
||||
where f (DbDate u1) u2 = max u1 u2 |
||||
f _ u = u |
||||
z = UTCTime d 0 |
||||
d = (toEnum (minBound :: Int) :: Day) |
||||
-- The problem here is that even this day is not the smallest possible, |
||||
-- because you can simply do `pred d` which gives a smaller day. So the |
||||
-- original solution is superior. |
||||
|
||||
-- 4 |
||||
sumDb :: [DatabaseItem] -> Integer |
||||
sumDb = sum . filterDbNumber |
||||
|
||||
-- with folds |
||||
sumDb' :: [DatabaseItem] -> Integer |
||||
sumDb' = foldr f 0 |
||||
where f (DbNumber i1) i2 = i1 + i2 |
||||
f _ i2 = i2 |
||||
|
||||
-- 5 |
||||
avgDb :: [DatabaseItem] -> Double |
||||
avgDb [] = undefined -- 0/0 |
||||
avgDb xs = (fromIntegral . sumDb $ xs ) / (fromIntegral . length $ xs) |
@ -0,0 +1,59 @@ |
||||
module Fibs where |
||||
|
||||
fibs :: [Integer] |
||||
fibs = 1 : scanl (+) 1 fibs |
||||
|
||||
-- To understand how this works, let's expand fibs, but first |
||||
-- the definition of scanl |
||||
-- |
||||
scanl' :: (a -> b -> a) -> a -> [b] -> [a] |
||||
scanl' f q ls = |
||||
q : (case ls of |
||||
[] -> [] |
||||
x:xs -> scanl' f (f q x) xs) |
||||
|
||||
-- Now on to the expansion, replace fibs with it's definition |
||||
fibs1 :: [Integer] |
||||
fibs1 = 1 : scanl (+) 1 (1 : scanl (+) 1 fibs) |
||||
|
||||
-- Apply definition of scanl |
||||
fibs2 :: [Integer] |
||||
fibs2 = 1 : (1 : scanl (+) 2 (scanl (+) 1 fibs)) |
||||
|
||||
-- As you can see we've constructed another element of the list |
||||
-- If we continue, we'll generate another one, but before we can do this |
||||
-- we have to repalce fibs with it's definition again. |
||||
-- Note: all fibs's are the same so you replace another fibs instead |
||||
fibs3 :: [Integer] |
||||
fibs3 = 1 : (1 : scanl (+) 2 (scanl (+) 1 |
||||
(1 : scanl (+) 1 fibs))) |
||||
|
||||
-- Apply the definition of the second scanl |
||||
fibs4 :: [Integer] |
||||
fibs4 = 1 : (1 : scanl (+) 2 (1 : scanl (+) 2 (scanl (+) 1 fibs))) |
||||
|
||||
-- Apply the definition of the first scanl |
||||
fibs5 :: [Integer] |
||||
fibs5 = 1 : (1 : (2 : (scanl (+) 3 (scanl (+) 2 (scanl (+) 1 fibs))))) |
||||
|
||||
-- We have now generated the first 3 elements of the fibonacci series |
||||
-- If the same replacements are being applied, it will keep generating |
||||
-- new entries. |
||||
|
||||
fibsN :: Int -> Integer |
||||
fibsN = (!!) fibs |
||||
|
||||
-- 1 |
||||
fibs' :: [Integer] |
||||
fibs' = take 20 $ fibs |
||||
|
||||
-- 2 |
||||
fibs'' :: [Integer] |
||||
fibs'' = takeWhile (<100) fibs |
||||
|
||||
-- 3 |
||||
fact :: [Integer] |
||||
fact = scanl (*) 1 [1..] |
||||
|
||||
factN :: Int -> Integer |
||||
factN = (!!) fact |
@ -0,0 +1,63 @@ |
||||
module Rewrite where |
||||
|
||||
-- 1 |
||||
myOr :: [Bool] -> Bool |
||||
myOr = foldr (||) False |
||||
|
||||
-- 2 |
||||
myAny :: (a -> Bool) -> [a] -> Bool |
||||
myAny f = foldr (\a b -> f a || b) False |
||||
|
||||
-- 3 |
||||
myElem :: Eq a => a -> [a] -> Bool |
||||
myElem x = foldr (\a b -> (x == a) || b) False |
||||
|
||||
myElem' :: Eq a => a -> [a] -> Bool |
||||
myElem' x = any (x==) |
||||
|
||||
-- 4 |
||||
myReverse :: [a] -> [a] |
||||
myReverse = foldr (\a b -> b ++ [a]) [] |
||||
-- other solution would be to use foldl (flip (:)) [] |
||||
|
||||
-- 5 |
||||
myMap :: (a -> b) -> [a] -> [b] |
||||
myMap f = foldr (\a b -> f a : b) [] |
||||
-- myMap = foldr ((:) . f) [] |
||||
|
||||
-- 6 |
||||
myFilter :: (a -> Bool) -> [a] -> [a] |
||||
myFilter f = foldr g [] |
||||
where g a b |
||||
| f a = a : b |
||||
| otherwise = b |
||||
|
||||
-- 7 |
||||
squish :: [[a]] -> [a] |
||||
squish = foldr (++) [] |
||||
|
||||
-- 8 |
||||
squishMap :: (a -> [b]) -> [a] -> [b] |
||||
squishMap f = foldr ((++) . f) [] |
||||
|
||||
-- 9 |
||||
squishAgain :: [[a]] -> [a] |
||||
squishAgain = squishMap id |
||||
|
||||
-- 10 |
||||
myMaximumBy :: (a -> a -> Ordering) -> [a] -> a |
||||
myMaximumBy _ [] = undefined |
||||
myMaximumBy _ [x] = x |
||||
myMaximumBy f (x:xs) = foldl go x xs |
||||
where go a b |
||||
| f a b == GT = a |
||||
| otherwise = b |
||||
|
||||
-- 11 |
||||
myMinimumBy :: (a -> a -> Ordering) -> [a] -> a |
||||
myMinimumBy _ [] = undefined |
||||
myMinimumBy _ [x] = x |
||||
myMinimumBy f (x:xs) = foldl go x xs |
||||
where go a b |
||||
| f a b == LT = a |
||||
| otherwise = b |
@ -0,0 +1,44 @@ |
||||
module WarmUp where |
||||
|
||||
-- 1 |
||||
stops :: [Char] |
||||
stops = "pbtdkg" |
||||
vowels :: [Char] |
||||
vowels = "aeiou" |
||||
|
||||
stopVowelStop :: [(Char, Char, Char)] |
||||
stopVowelStop = [(x,y,z) | x <- stops, y <- vowels, z <- stops] |
||||
|
||||
svsP :: [(Char, Char, Char)] |
||||
svsP = [(x,y,z) | x <- stops, y <- vowels, z <- stops, x == 'p'] |
||||
-- or |
||||
svsP' :: [(Char, Char, Char)] |
||||
svsP' = [('p', x, y) | x <- vowels, y <- stops] |
||||
-- or |
||||
svsP'' :: [(Char, Char, Char)] |
||||
svsP'' = filter (\(x,_,_) -> x == 'p') stopVowelStop |
||||
|
||||
nouns :: [[Char]] |
||||
nouns = ["apple", "dog", "door", "water", "life"] |
||||
|
||||
verbs :: [[Char]] |
||||
verbs = ["eat", "run", "drop", "jump", "grow"] |
||||
|
||||
tupleUp :: [a] -> [b] -> [c] -> [(a,b,c)] |
||||
tupleUp xs ys zs = [(x,y,z) | x <- xs, y <- ys, z <- zs] |
||||
|
||||
nvn :: [([Char], [Char], [Char])] |
||||
nvn = tupleUp nouns verbs nouns |
||||
|
||||
-- 2 |
||||
seekritFunc :: [Char] -> Int |
||||
seekritFunc x = div (sum (map length (words x))) |
||||
(length (words x)) |
||||
-- This function returns average number of characters per word |
||||
|
||||
-- 3 |
||||
seekritFunc' :: Fractional a => [Char] -> a |
||||
seekritFunc' x = (/) (fromIntegral $ sum (map length (words x))) |
||||
(fromIntegral $ length (words x)) |
||||
|
||||
|
@ -0,0 +1,10 @@ |
||||
# Exercises: Pity the Bool |
||||
## Exercise 1 |
||||
The cardinality here is 4: `[Big False, Big True, Small False, Small True]` |
||||
|
||||
## Exercise 2 |
||||
Cardinality of `Numba` is 256 (same as `Int8`). |
||||
Cardinality of `Bool` is 2. |
||||
Cardinality of `NumberOrBool` is `256 + 2 = 258` |
||||
|
||||
If you try to create a `Numba` with numeric literals larger than 127 or smaller than (-128) you will get an error as the number will be higher than what can be contained in an `Int8` |
@ -0,0 +1,2 @@ |
||||
# Exercises: How Does Your Garden Grow? |
||||
see src/garden.hs |
@ -0,0 +1,2 @@ |
||||
# Exercises: Programmers |
||||
see src/programmers.hs |
@ -0,0 +1,18 @@ |
||||
# Exercises: The Quad |
||||
# Exercise 1 |
||||
`Either` is a sum type, so `eQuad` has `4 + 4 = 8` inhabitants. |
||||
|
||||
# Exercise 2 |
||||
`(,)` is a product type, so `prodQuad` has `4 * 4 = 16` inhabitants. |
||||
|
||||
# Exercise 3 |
||||
`(->)` is an exponential type, so `funcQuad` has `4 ^ 4 = 256` inhabitants. |
||||
|
||||
# Exercise 4 |
||||
`(,,)` is a product type, so `prodTBool` has `2 * 2 * 2 = 8` inhabitants. |
||||
|
||||
# Exercise 5 |
||||
`(->)` is an exponential type, so `gTwo` has `(2 ^ 2) ^ 2 = 16` inhabitants. |
||||
|
||||
# Exercise 6 |
||||
`(->)` is an exponential type, so `fTwo` has `(4 ^ 4) ^ 2 = 65536` inhabitants. |
@ -0,0 +1,21 @@ |
||||
# Chapter Exercise |
||||
## Multiple Choice |
||||
1. (a) |
||||
2. (c) |
||||
3. (b) |
||||
4. (c) |
||||
|
||||
## Ciphers |
||||
see src/vigenere.hs |
||||
|
||||
## As-patterns |
||||
see src/aspatterns.hs |
||||
|
||||
## Language exercises |
||||
see src/langexerc.hs |
||||
|
||||
## Phone excercise |
||||
see src/phone.hs |
||||
|
||||
## Hutton's Razor |
||||
see src/huttonrazor.hs |
@ -0,0 +1,10 @@ |
||||
# Exercises: Dog Types |
||||
1. Type constructor |
||||
2. `* -> *` |
||||
3. `*` |
||||
4. `Num a => Doggies a` |
||||
5. `Doggies Integer` |
||||
6. `Doggies [Char]` |
||||
7. Both. |
||||
8. `a -> DogueDeBordeaux a` |
||||
9. `DogueDeBordeaux [Char]` |
@ -0,0 +1,2 @@ |
||||
# Exercise : Vehicles |
||||
see src/vehicle.hs |
@ -0,0 +1,6 @@ |
||||
# Exercises: Cardinality |
||||
1. 1 |
||||
2. 3 |
||||
3. 65536 |
||||
4. `Integer` is unbounded. |
||||
5. 8 bits represent 256 value (2 to the power of 8) |
@ -0,0 +1,4 @@ |
||||
# Exercises: For Example |
||||
1. Type is `Example` `MakeExample :: Example` |
||||
2. It shows deriving of the typeclass `Show` |
||||
3. It will change to `Int -> Example` |
@ -0,0 +1,2 @@ |
||||
# Exercises: Logic Goats |
||||
see src/logicgoats.hs |
@ -0,0 +1,16 @@ |
||||
module AsPatters where |
||||
|
||||
import Data.Char |
||||
|
||||
isSubseqOff :: (Eq a) => [a] -> [a] -> Bool |
||||
isSubseqOff [] _ = True |
||||
isSubseqOff _ [] = False |
||||
isSubseqOff s1@(x:xs) (y:ys) = |
||||
if x == y |
||||
then isSubseqOff xs ys |
||||
else isSubseqOff s1 ys |
||||
|
||||
capitalizeWords :: String -> [(String, String)] |
||||
capitalizeWords = map f . words |
||||
where f [] = ([],[]) |
||||
f s@(x:xs) = (s, toUpper x : xs) |
@ -0,0 +1,81 @@ |
||||
module BinaryTree where |
||||
|
||||
data BinaryTree a = Leaf |
||||
| Node (BinaryTree a) a (BinaryTree a) |
||||
deriving (Show, Eq, Ord) |
||||
|
||||
insert' :: Ord a => a -> BinaryTree a -> BinaryTree a |
||||
insert' b Leaf = Node Leaf b Leaf |
||||
insert' b (Node left a right) |
||||
| b == a = Node left a right |
||||
| b < a = Node (insert' b left) a right |
||||
| b > a = Node left a (insert' b right) |
||||
|
||||
-- map |
||||
mapTree :: (a -> b) -> BinaryTree a -> BinaryTree b |
||||
mapTree _ Leaf = Leaf |
||||
mapTree f (Node left a right) = Node (mapTree f left) |
||||
(f a) |
||||
(mapTree f right) |
||||
|
||||
testTree' :: BinaryTree Integer |
||||
testTree' = Node (Node Leaf 3 Leaf) 1 (Node Leaf 4 Leaf) |
||||
|
||||
mapExpected :: BinaryTree Integer |
||||
mapExpected = Node (Node Leaf 4 Leaf) 2 (Node Leaf 5 Leaf) |
||||
|
||||
mapOkay :: IO () |
||||
mapOkay = |
||||
if mapTree (+1) testTree' == mapExpected |
||||
then print "yup okay!" |
||||
else error "test failed!" |
||||
|
||||
-- lists |
||||
preorder :: BinaryTree a -> [a] |
||||
preorder Leaf = [] |
||||
preorder (Node left a right) = |
||||
a : (preorder left ++ preorder right) |
||||
|
||||
inorder :: BinaryTree a -> [a] |
||||
inorder Leaf = [] |
||||
inorder (Node left a right) = |
||||
inorder left ++ (a : inorder right) |
||||
|
||||
postorder :: BinaryTree a -> [a] |
||||
postorder Leaf = [] |
||||
postorder (Node left a right) = |
||||
postorder left ++ postorder right ++ [a] |
||||
|
||||
testTree :: BinaryTree Integer |
||||
testTree = Node (Node Leaf 1 Leaf) 2 (Node Leaf 3 Leaf) |
||||
|
||||
testPreorder :: IO () |
||||
testPreorder = |
||||
if preorder testTree == [2, 1, 3] |
||||
then putStrLn "Preorder fine!" |
||||
else putStrLn "Bad news bears." |
||||
|
||||
testInorder :: IO () |
||||
testInorder = |
||||
if inorder testTree == [1, 2, 3] |
||||
then putStrLn "Inorder fine!" |
||||
else putStrLn "Bad news bears." |
||||
|
||||
testPostorder :: IO () |
||||
testPostorder = |
||||
if postorder testTree == [1, 3, 2] |
||||
then putStrLn "Postorder fine!" |
||||
else putStrLn "Bad news bears." |
||||
|
||||
main :: IO () |
||||
main = do |
||||
testPreorder |
||||
testInorder |
||||
testPostorder |
||||
|
||||
|
||||
-- foldr |
||||
foldTree :: (a -> b -> b) -> b -> BinaryTree a -> b |
||||
foldTree _ b Leaf = b |
||||
foldTree f b (Node left a right) = |
||||
f a (foldTree f (foldTree f b right) left) |
@ -0,0 +1,18 @@ |
||||
module Garden where |
||||
|
||||
data FlowerType = Gardenia |
||||
| Daisy |
||||
| Rose |
||||
| Lilac |
||||
deriving Show |
||||
|
||||
type Gardener = String |
||||
|
||||
data Garden = Garden Gardener FlowerType deriving Show |
||||
|
||||
-- 1 |
||||
data Garden' = Gardenia' Gardener |
||||
| Daisy' Gardener |
||||
| Rose' Gardener |
||||
| Lilac' Gardener |
||||
deriving Show |
@ -0,0 +1,14 @@ |
||||
module HuttonRazor where |
||||
|
||||
data Expr = Lit Integer | Add Expr Expr |
||||
|
||||
-- 1 |
||||
eval :: Expr -> Integer |
||||
eval (Lit i) = i |
||||
eval (Add e1 e2) = eval e1 + eval e2 |
||||
|
||||
-- 2 |
||||
printExpr :: Expr -> String |
||||
printExpr (Lit i) = show i |
||||
printExpr (Add e1 e2) = |
||||
printExpr e1 ++ " + " ++ printExpr e2 |
@ -0,0 +1,18 @@ |
||||
module LangExerc where |
||||
|
||||
import Data.Char |
||||
import Data.List |
||||
|
||||
-- 1 |
||||
capitalizeWord :: String -> String |
||||
capitalizeWord [] = [] |
||||
capitalizeWord (x:xs) = toUpper x : xs |
||||
|
||||
-- 2 |
||||
capitalizeParagraph :: String -> String |
||||
capitalizeParagraph = unwords . (foo True) . words |
||||
where foo _ [] = [] |
||||
foo True (x:xs) = capitalizeWord x : go (x,xs) |
||||
foo False (x:xs) = x : go (x,xs) |
||||
go (_, []) = [] |
||||
go (x,xs) = foo (isSuffixOf "." x) xs |
@ -0,0 +1,25 @@ |
||||
{-# LANGUAGE FlexibleInstances #-} |
||||
|
||||
module LogicGoats where |
||||
|
||||
class TooMany a where |
||||
tooMany :: a -> Bool |
||||
|
||||
-- 1 |
||||
newtype IntString = IntString (Int,String) |
||||
instance TooMany IntString where |
||||
tooMany (IntString (n, _)) = n > 42 |
||||
|
||||
-- needs FlexibleInstances |
||||
instance TooMany (Int, String) where |
||||
tooMany (n, _) = n > 42 |
||||
|
||||
-- 2 |
||||
newtype Goats = Goats (Int, Int) |
||||
instance TooMany Goats where |
||||
tooMany (Goats (m, n)) = (m+n)>42 |
||||
|
||||
--3 |
||||
-- needs FlexibleInstances |
||||
instance (Num a, TooMany a) => TooMany (a,a) where |
||||
tooMany (a,b) = tooMany $ a + b |
@ -0,0 +1,159 @@ |
||||
module Phone where |
||||
-- This was quite a doozy. |
||||
-- It's far from perfect, but it does the job. |
||||
import Data.Char |
||||
import Data.List |
||||
|
||||
convo :: [String] |
||||
convo = |
||||
["Wanna play 20 questions", |
||||
"Ya", |
||||
"U 1st haha", |
||||
"Lol ok. Have u ever tasted alcohol", |
||||
"Lol ya", |
||||
"Wow ur cool haha. Ur turn", |
||||
"Ok. Do u think I am pretty Lol", |
||||
"Lol ya", |
||||
"Just making sure rofl ur turn"] |
||||
|
||||
|
||||
-- validButtons = "1234567890*#" |
||||
type Digit = Char |
||||
|
||||
-- Valid presses: 1 and up |
||||
type Presses = Int |
||||
|
||||
contains :: Char -> (Digit, String) -> Bool |
||||
contains c (d,s) = c `elem` d : s |
||||
|
||||
-- Two types of buttons |
||||
-- A bit silly that you have to now pattern match |
||||
-- on each of them, even though they have the same |
||||
-- 'content'. Better solution? It is a sum of products |
||||
-- rather than a product of sums though... |
||||
data Button = Normal (Digit, String) |
||||
| Capital (Digit, String) |
||||
deriving (Eq, Show) |
||||
|
||||
-- Some buttons |
||||
isCapital :: Button -> Bool |
||||
isCapital (Capital _) = True |
||||
isCapital _ = False |
||||
|
||||
isNormal :: Button -> Bool |
||||
isNormal (Normal _) = True |
||||
isNormal _ = False |
||||
|
||||
contains' :: Char -> Button -> Bool |
||||
contains' c (Capital a) = contains c a |
||||
contains' c (Normal a) = contains c a |
||||
|
||||
getDigit :: Button -> Digit |
||||
getDigit (Capital (d,_)) = d |
||||
getDigit (Normal (d, _)) = d |
||||
|
||||
getPresses :: Char -> (Digit, String) -> Maybe Presses |
||||
getPresses c (d,s) = fmap (+1) $ elemIndex c (d:s) |
||||
|
||||
getPresses' :: Char -> Button -> Maybe Presses |
||||
getPresses' c (Capital a) = getPresses c a |
||||
getPresses' c (Normal a) = getPresses c a |
||||
|
||||
-- 1 The phone is just a list of buttons |
||||
data DaPhone = DaPhone [Button] deriving (Eq, Show) |
||||
|
||||
getCapital :: DaPhone -> Maybe Button |
||||
getCapital (DaPhone xs) = find isCapital xs |
||||
|
||||
lookUp :: DaPhone -> Char -> Maybe Button |
||||
lookUp (DaPhone xs) c = find (contains' c) xs |
||||
|
||||
-- A valid phone has one Capital |
||||
-- button |
||||
mkDaPhone :: [Button] -> Maybe DaPhone |
||||
mkDaPhone xs |
||||
| req = Just $ DaPhone xs |
||||
| otherwise = Nothing |
||||
where req = (length $ filter isCapital xs) == 1 |
||||
|
||||
-- 2 |
||||
reverseTaps :: DaPhone -> Char -> [(Digit, Presses)] |
||||
reverseTaps p c |
||||
| isUpper c = (cap $ getCapital p) ++ (reverseTaps p $ toLower c) |
||||
| otherwise = go m |
||||
where cap Nothing = [] |
||||
cap (Just b) = [(getDigit b, 1)] |
||||
m = lookUp p c |
||||
go Nothing = [] |
||||
go (Just b) = blah (getPresses' c b) $ (,) $ getDigit b |
||||
blah Nothing _ = [] |
||||
blah (Just a) f = f a : [] |
||||
|
||||
cellPhonesDead :: DaPhone -> String -> [(Digit, Presses)] |
||||
cellPhonesDead p = concat . map (reverseTaps p) |
||||
|
||||
-- 3 |
||||
fingerTaps :: [(Digit, Presses)] -> Presses |
||||
fingerTaps = foldr (\(_,p) z -> p + z) 0 |
||||
|
||||
count :: Eq a => a -> [a] -> Int |
||||
count c s = length $ filter (==c) s |
||||
|
||||
countElements :: Eq a => [a] -> [(a, Int)] |
||||
countElements s = map (\c -> (c,(count c s))) $ nub s |
||||
|
||||
-- 4 |
||||
mostPopular :: (Eq a, Ord a) => [a] -> a |
||||
mostPopular s = fst m |
||||
where d = countElements s |
||||
m = maximumBy (\(_,i) (_,j) -> compare i j) d |
||||
|
||||
myPhone :: Maybe DaPhone |
||||
myPhone = mkDaPhone |
||||
[ Normal ('1', ""), |
||||
Normal ('2', "abc"), |
||||
Normal ('3', "def"), |
||||
Normal ('4', "ghi"), |
||||
Normal ('5', "jkl"), |
||||
Normal ('6', "mno"), |
||||
Normal ('7', "pqrs"), |
||||
Normal ('8', "tuv"), |
||||
Normal ('9', "wvxz"), |
||||
Normal ('0', "+ "), |
||||
Capital ('*', "^"), |
||||
Normal ('#', ".,")] |
||||
|
||||
-- Applies cellPhonesdead on myPhone |
||||
-- Keep in mind that myPhone is of type Maybe DaPhone |
||||
-- That is why we use fmap to act on the DaPhone |
||||
-- inside the Maybe 'container' (i.e. Functor) |
||||
-- |
||||
-- fmap cellPhonesDead myPhone :: Maybe (String -> [(Digit, Presses)]) |
||||
-- to which we now want to provide a string, so we need a map that |
||||
-- maps (String -> [(Digit, Presses)]) to [(Digit, Presses)]. |
||||
-- In order to do that we want to provide a map that applies a String |
||||
-- to the contained function. |
||||
getDP :: String -> Maybe [(Digit, Presses)] |
||||
getDP s = fmap (\f -> f s) $ fmap cellPhonesDead myPhone |
||||
|
||||
-- Same deal as getDp, a bit more complex. |
||||
getFT :: String -> Maybe Presses |
||||
getFT s = fmap fingerTaps fil |
||||
where fil = getDP (filter (==(mostPopular s)) s) |
||||
|
||||
-- 5 |
||||
coolestLtr :: [String] -> Char |
||||
coolestLtr = mostPopular . concat |
||||
|
||||
coolestWord :: [String] -> String |
||||
coolestWord = mostPopular |
||||
|
||||
-- coolestWord :: [String] -> String |
||||
|
||||
main :: IO () |
||||
main = do |
||||
print $ map getDP convo |
||||
print $ map mostPopular convo |
||||
print $ map getFT convo |
||||
print $ coolestLtr convo |
||||
print $ coolestWord ((words . concat) convo) |
@ -0,0 +1,40 @@ |
||||
module Programmers where |
||||
|
||||
data OperatingSystem = |
||||
GnuPlusLinux |
||||
| OpenBSDPlusNevermindJustBSDStill |
||||
| Mac |
||||
| Windows |
||||
deriving (Eq, Show) |
||||
|
||||
data ProgLang = |
||||
Haskell |
||||
| Agda |
||||
| Idris |
||||
| PureScript |
||||
deriving (Eq, Show) |
||||
|
||||
data Programmer = |
||||
Programmer { os :: OperatingSystem |
||||
, lang :: ProgLang } |
||||
deriving (Eq, Show) |
||||
|
||||
-- Programmers |
||||
allOperatingSystems :: [OperatingSystem] |
||||
allOperatingSystems = |
||||
[ GnuPlusLinux |
||||
, OpenBSDPlusNevermindJustBSDStill |
||||
, Mac |
||||
, Windows |
||||
] |
||||
|
||||
allLanguages :: [ProgLang] |
||||
allLanguages = [Haskell, Agda, Idris, PureScript] |
||||
|
||||
allProgrammers :: [Programmer] |
||||
allProgrammers = |
||||
[Programmer x y | x <- allOperatingSystems, y <- allLanguages ] |
||||
|
||||
check :: Bool |
||||
check = length allProgrammers == |
||||
length allOperatingSystems * length allLanguages |
@ -0,0 +1,56 @@ |
||||
module Vehicle where |
||||
|
||||
data Price = Price Integer deriving (Eq, Show) |
||||
data Manufacturer = Mini | Mazda | Tata deriving (Eq, Show) |
||||
data Airline = PapuAir | CatapultsR'Us | TakeYourChancesUnited |
||||
deriving (Eq, Show) |
||||
|
||||
data Vehicle = Car Manufacturer Price | Plane Airline deriving (Eq, Show) |
||||
|
||||
myCar :: Vehicle |
||||
myCar = Car Mini (Price 14000) |
||||
|
||||
urCar :: Vehicle |
||||
urCar = Car Mazda (Price 20000) |
||||
|
||||
clownCar :: Vehicle |
||||
clownCar = Car Tata (Price 7000) |
||||
|
||||
doge :: Vehicle |
||||
doge = Plane PapuAir |
||||
|
||||
-- 1 |
||||
-- myCar :: Vehicle |
||||
|
||||
-- 2 |
||||
isCar :: Vehicle -> Bool |
||||
isCar (Car _ _) = True |
||||
isCar _ = False |
||||
|
||||
isPlane :: Vehicle -> Bool |
||||
isPlane (Plane _) = True |
||||
isPlane _ = False |
||||
|
||||
areCars :: [Vehicle] -> [Bool] |
||||
areCars = map isCar |
||||
|
||||
-- 3 |
||||
getManu :: Vehicle -> Manufacturer |
||||
getManu (Car m _) = m |
||||
getManu _ = undefined |
||||
|
||||
-- 4 |
||||
-- Would generate a partial function error. |
||||
-- You could change it to a Maybe Manufacturer and return Nothing |
||||
|
||||
-- 5 |
||||
data Size = Size Integer deriving (Eq, Show) |
||||
data Vehicle' = Car' Manufacturer | Plane' Airline Size deriving (Eq, Show) |
||||
doge' :: Vehicle' |
||||
doge' = Plane' PapuAir (Size 120) |
||||
-- isCar stays the same |
||||
isPlane' :: Vehicle' -> Bool |
||||
isPlane' (Plane' _ _) = True |
||||
isPlane' _ = False |
||||
-- areCars stays the same |
||||
-- getManu stays the same |
@ -0,0 +1,37 @@ |
||||
module Vigenere where |
||||
|
||||
import Data.Char |
||||
|
||||
alphaIndex :: Char -> Int |
||||
alphaIndex c |
||||
| elem c ['a'..'z'] = ord c - ord 'a' |
||||
| elem c ['A'..'Z'] = ord c - ord 'A' |
||||
| otherwise = 0 |
||||
|
||||
|
||||
shift :: Char -> Char -> Char |
||||
shift c k |
||||
| elem c ['a'..'z'] = go c k 'a' |
||||
| elem c ['A'..'Z'] = go c k 'A' |
||||
| otherwise = c |
||||
where go p key base = chr $ (mod rel r) + b |
||||
where rel = alphaIndex p + alphaIndex key |
||||
r = 26 |
||||
b = ord base |
||||
|
||||
-- nice solution, but maps keyword to non-alpha characters |
||||
-- e.g. MEET_AT_DAWN |
||||
-- ALLYALLYALLY |
||||
vigenere' :: [Char] -> [Char] -> [Char] |
||||
vigenere' xs ys = zipWith shift xs ((concat . repeat) ys) |
||||
|
||||
-- wrote own zipWith variant which maps only when isAlpha |
||||
vigenere :: [Char] -> [Char] -> [Char] |
||||
vigenere xs [] = xs -- necessary to avoid bottom |
||||
vigenere xs ys = myZipWith shift xs ys |
||||
where myZipWith _ [] _ = [] |
||||
myZipWith f s [] = myZipWith f s ys |
||||
myZipWith f (a:as) k@(b:bs) = |
||||
if isAlpha a |
||||
then f a b : myZipWith f as bs |
||||
else a : myZipWith f as k |
Loading…
Reference in new issue