Skip to content

Commit a1c9804

Browse files
committed
Complete Set15
1 parent 662e71b commit a1c9804

File tree

1 file changed

+60
-18
lines changed

1 file changed

+60
-18
lines changed

Set15.hs

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Text.Read (readMaybe)
1717
-- sumTwoMaybes Nothing Nothing ==> Nothing
1818

1919
sumTwoMaybes :: Maybe Int -> Maybe Int -> Maybe Int
20-
sumTwoMaybes = todo
20+
sumTwoMaybes = liftA2 (+)
2121

2222
------------------------------------------------------------------------------
2323
-- Ex 2: Given two lists of words, xs and ys, generate all statements
@@ -36,7 +36,7 @@ sumTwoMaybes = todo
3636
-- "code is not suffering","code is not life"]
3737

3838
statements :: [String] -> [String] -> [String]
39-
statements = todo
39+
statements xs ys = liftA2 (\x y -> [x ++ " is " ++ y, x ++ " is not " ++ y]) xs ys >>= id
4040

4141
------------------------------------------------------------------------------
4242
-- Ex 3: A simple calculator with error handling. Given an operation
@@ -54,7 +54,7 @@ statements = todo
5454
-- calculator "double" "7x" ==> Nothing
5555

5656
calculator :: String -> String -> Maybe Int
57-
calculator = todo
57+
calculator op x = liftA2 ($) (lookup op [("negate", negate), ("double", (*2))]) (readMaybe x)
5858

5959
------------------------------------------------------------------------------
6060
-- Ex 4: Safe division. Implement the function validateDiv that
@@ -71,7 +71,7 @@ calculator = todo
7171
-- validateDiv 0 3 ==> Ok 0
7272

7373
validateDiv :: Int -> Int -> Validation Int
74-
validateDiv = todo
74+
validateDiv x y = check (y /= 0) "Division by zero!" (x `div` y)
7575

7676
------------------------------------------------------------------------------
7777
-- Ex 5: Validating street addresses. A street address consists of a
@@ -101,7 +101,10 @@ data Address = Address String String String
101101
deriving (Show,Eq)
102102

103103
validateAddress :: String -> String -> String -> Validation Address
104-
validateAddress streetName streetNumber postCode = todo
104+
validateAddress streetName streetNumber postCode =
105+
Address <$> check (length streetName <= 20) "Invalid street name" streetName
106+
<*> check (all isDigit streetNumber) "Invalid street number" streetNumber
107+
<*> check (length postCode == 5 && all isDigit postCode) "Invalid postcode" postCode
105108

106109
------------------------------------------------------------------------------
107110
-- Ex 6: Given the names, ages and employment statuses of two
@@ -123,7 +126,10 @@ data Person = Person String Int Bool
123126
twoPersons :: Applicative f =>
124127
f String -> f Int -> f Bool -> f String -> f Int -> f Bool
125128
-> f [Person]
126-
twoPersons name1 age1 employed1 name2 age2 employed2 = todo
129+
twoPersons name1 age1 employed1 name2 age2 employed2 =
130+
liftA2 (\p1 p2 -> [p1, p2])
131+
(Person <$> name1 <*> age1 <*> employed1)
132+
(Person <$> name2 <*> age2 <*> employed2)
127133

128134
------------------------------------------------------------------------------
129135
-- Ex 7: Validate a String that's either a Bool or an Int. The return
@@ -143,7 +149,14 @@ twoPersons name1 age1 employed1 name2 age2 employed2 = todo
143149
-- boolOrInt "Falseb" ==> Errors ["Not a Bool","Not an Int"]
144150

145151
boolOrInt :: String -> Validation (Either Bool Int)
146-
boolOrInt = todo
152+
boolOrInt s =
153+
(Left <$> checkBool s) <|> (Right <$> checkInt s)
154+
where
155+
checkBool str = check (str == "True" || str == "False") "Not a Bool" (read str :: Bool)
156+
checkInt str = check (isJust (readMaybe str :: Maybe Int)) "Not an Int" (fromJust (readMaybe str :: Maybe Int))
157+
isJust (Just _) = True
158+
isJust Nothing = False
159+
fromJust (Just x) = x
147160

148161
------------------------------------------------------------------------------
149162
-- Ex 8: Improved phone number validation. Implement the function
@@ -167,7 +180,10 @@ boolOrInt = todo
167180
-- ==> Errors ["Too long"]
168181

169182
normalizePhone :: String -> Validation String
170-
normalizePhone = todo
183+
normalizePhone s =
184+
let stripped = filter (not . isSpace) s
185+
in check (length stripped <= 10) "Too long" stripped
186+
*> traverse (\c -> check (isDigit c) ("Invalid character: " ++ [c]) c) stripped
171187

172188
------------------------------------------------------------------------------
173189
-- Ex 9: Parsing expressions. The Expression type describes an
@@ -211,7 +227,33 @@ data Expression = Plus Arg Arg | Minus Arg Arg
211227
deriving (Show, Eq)
212228

213229
parseExpression :: String -> Validation Expression
214-
parseExpression = todo
230+
parseExpression s =
231+
case words s of
232+
[s1, op, s2] ->
233+
let opV = parseOp op
234+
arg1V = parseArg s1
235+
arg2V = parseArg s2
236+
in opV <*> arg1V <*> arg2V
237+
_ -> invalid ("Invalid expression: " ++ s)
238+
239+
parseOp :: String -> Validation (Arg -> Arg -> Expression)
240+
parseOp op =
241+
case op of
242+
"+" -> pure Plus
243+
"-" -> pure Minus
244+
_ -> invalid ("Unknown operator: " ++ op)
245+
246+
parseArg :: String -> Validation Arg
247+
parseArg s =
248+
let n = readMaybe s :: Maybe Int
249+
v = maybe (invalid ("Invalid number: " ++ s)) (pure . Number) n
250+
isVar = length s == 1 && isAlpha (head s)
251+
var = if isVar then pure (Variable (head s)) else invalid ("Invalid variable: " ++ s)
252+
in case (n, isVar) of
253+
(Just n, _) -> pure (Number n)
254+
(_, True) -> pure (Variable (head s))
255+
_ -> v *> var
256+
215257

216258
------------------------------------------------------------------------------
217259
-- Ex 10: The Priced T type tracks a value of type T, and a price
@@ -236,11 +278,11 @@ data Priced a = Priced Int a
236278
deriving (Show, Eq)
237279

238280
instance Functor Priced where
239-
fmap = todo
281+
fmap f (Priced p x) = Priced p (f x)
240282

241283
instance Applicative Priced where
242-
pure = todo
243-
liftA2 = todo
284+
pure = Priced 0
285+
liftA2 f (Priced p1 x1) (Priced p2 x2) = Priced (p1 + p2) (f x1 x2)
244286

245287
------------------------------------------------------------------------------
246288
-- Ex 11: This and the next exercise will use a copy of the
@@ -273,7 +315,7 @@ instance MyApplicative [] where
273315
myLiftA2 = liftA2
274316

275317
(<#>) :: MyApplicative f => f (a -> b) -> f a -> f b
276-
f <#> x = todo
318+
f <#> x = myLiftA2 ($) f x
277319

278320
------------------------------------------------------------------------------
279321
-- Ex 12: Reimplement fmap using liftA2 and pure. In practical terms,
@@ -290,7 +332,7 @@ f <#> x = todo
290332
-- myFmap negate [1,2,3] ==> [-1,-2,-3]
291333

292334
myFmap :: MyApplicative f => (a -> b) -> f a -> f b
293-
myFmap = todo
335+
myFmap f x = myPure f <#> x
294336

295337
------------------------------------------------------------------------------
296338
-- Ex 13: Given a function that returns an Alternative value, and a
@@ -317,7 +359,7 @@ myFmap = todo
317359
-- ==> Errors ["zero","zero","zero"]
318360

319361
tryAll :: Alternative f => (a -> f b) -> [a] -> f b
320-
tryAll = todo
362+
tryAll f = foldr ((<|>) . f) empty
321363

322364
------------------------------------------------------------------------------
323365
-- Ex 14: Here's the type `Both` that expresses the composition of
@@ -342,7 +384,7 @@ newtype Both f g a = Both (f (g a))
342384
deriving Show
343385

344386
instance (Functor f, Functor g) => Functor (Both f g) where
345-
fmap = todo
387+
fmap f (Both x) = Both (fmap (fmap f) x)
346388

347389
------------------------------------------------------------------------------
348390
-- Ex 15: The composition of two Applicatives is also an Applicative!
@@ -370,5 +412,5 @@ instance (Functor f, Functor g) => Functor (Both f g) where
370412
-- Errors ["fail 1","fail 2"]]
371413

372414
instance (Applicative f, Applicative g) => Applicative (Both f g) where
373-
pure = todo
374-
liftA2 = todo
415+
pure x = Both (pure (pure x))
416+
liftA2 f (Both x) (Both y) = Both (liftA2 (liftA2 f) x y)

0 commit comments

Comments
 (0)