-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Solutions for Lecture 2 exercises #2
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -40,6 +40,9 @@ module Lecture2 | |||||||||||||||||||||||||
, constantFolding | ||||||||||||||||||||||||||
) where | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import Data.Char ( isSpace ) | ||||||||||||||||||||||||||
import Data.List ( sort ) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Implement a function that finds a product of all the numbers in | ||||||||||||||||||||||||||
the list. But implement a lazier version of this function: if you see | ||||||||||||||||||||||||||
zero, you can stop calculating product and return 0 immediately. | ||||||||||||||||||||||||||
|
@@ -48,7 +51,13 @@ zero, you can stop calculating product and return 0 immediately. | |||||||||||||||||||||||||
84 | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
lazyProduct :: [Int] -> Int | ||||||||||||||||||||||||||
lazyProduct = error "TODO" | ||||||||||||||||||||||||||
lazyProduct [] = 1 | ||||||||||||||||||||||||||
lazyProduct list = go 1 list | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
go :: Int -> [Int] -> Int | ||||||||||||||||||||||||||
go 0 _ = 0 | ||||||||||||||||||||||||||
go result [] = result | ||||||||||||||||||||||||||
go result (x : xs) = go (result * x) xs | ||||||||||||||||||||||||||
Comment on lines
+58
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a very clever solution! 🧠 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks :-) In the past, I have mostly been reading about Haskell, haven't written any code. I have come across BangPatterns and strict evaluation in my readings, but never applied them as I've not actually written any Haskell code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. by the way @chshersh curious, how do you get these cool emojis in your comments? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kpadmasola You can start typing : and then the name of an emoji. GitHub has some autocompletion for emoji names 🙂 Like |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Implement a function that duplicates every element in the list. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -58,7 +67,8 @@ lazyProduct = error "TODO" | |||||||||||||||||||||||||
"ccaabb" | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
duplicate :: [a] -> [a] | ||||||||||||||||||||||||||
duplicate = error "TODO" | ||||||||||||||||||||||||||
duplicate [] = [] | ||||||||||||||||||||||||||
duplicate (x : xs) = x : x : duplicate xs | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Implement function that takes index and a list and removes the | ||||||||||||||||||||||||||
element at the given position. Additionally, this function should also | ||||||||||||||||||||||||||
|
@@ -70,7 +80,16 @@ return the removed element. | |||||||||||||||||||||||||
>>> removeAt 10 [1 .. 5] | ||||||||||||||||||||||||||
(Nothing,[1,2,3,4,5]) | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
removeAt = error "TODO" | ||||||||||||||||||||||||||
removeAt :: Int -> [a] -> (Maybe a, [a]) | ||||||||||||||||||||||||||
removeAt _ [] = (Nothing, []) | ||||||||||||||||||||||||||
removeAt index list | ||||||||||||||||||||||||||
| index < 0 = (Nothing, list) | ||||||||||||||||||||||||||
| otherwise = (element, headPart ++ tailPart) | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
headPart = take index list | ||||||||||||||||||||||||||
(element, tailPart) = case drop index list of | ||||||||||||||||||||||||||
Comment on lines
+89
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a correct implementation 👍🏻 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point. |
||||||||||||||||||||||||||
[] -> (Nothing, []) | ||||||||||||||||||||||||||
(x: xs) -> (Just x, xs) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Write a function that takes a list of lists and returns only | ||||||||||||||||||||||||||
lists of even lengths. | ||||||||||||||||||||||||||
|
@@ -81,7 +100,8 @@ lists of even lengths. | |||||||||||||||||||||||||
♫ NOTE: Use eta-reduction and function composition (the dot (.) operator) | ||||||||||||||||||||||||||
in this function. | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
evenLists = error "TODO" | ||||||||||||||||||||||||||
evenLists :: [[a]] -> [[a]] | ||||||||||||||||||||||||||
evenLists = filter (even . length) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | The @dropSpaces@ function takes a string containing a single word | ||||||||||||||||||||||||||
or number surrounded by spaces and removes all leading and trailing | ||||||||||||||||||||||||||
|
@@ -97,7 +117,8 @@ spaces. | |||||||||||||||||||||||||
|
||||||||||||||||||||||||||
🕯 HINT: look into Data.Char and Prelude modules for functions you may use. | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
dropSpaces = error "TODO" | ||||||||||||||||||||||||||
dropSpaces :: String -> String | ||||||||||||||||||||||||||
dropSpaces = takeWhile (not . isSpace) . dropWhile isSpace | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perfect 🏆 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks ! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chshersh One doubt I have is, how does this efficiently handle infinite trailing spaces? I noticed that there is a testcase for that. Without looking at all the infinite trailing spaces, how can this work? Is it some kind of fusion or something like that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no special magic 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, thanks! |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -159,8 +180,43 @@ data Knight = Knight | |||||||||||||||||||||||||
, knightAttack :: Int | ||||||||||||||||||||||||||
, knightEndurance :: Int | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
dragonFight = error "TODO" | ||||||||||||||||||||||||||
data Color | ||||||||||||||||||||||||||
= Red | ||||||||||||||||||||||||||
| Green | ||||||||||||||||||||||||||
| Black | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
data Treasure | ||||||||||||||||||||||||||
= Gold | ||||||||||||||||||||||||||
| GoldPlusTreasure | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I clarified the task a bit to cover cases so dragons would determine the treasure itself, not just "yes" or "no". It will make the implementation a bit more challenging 😅 But, besides that, the current implementation covers the requirements nicely 👍🏻 |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
data Dragon = Dragon | ||||||||||||||||||||||||||
{ dragonColor :: Color | ||||||||||||||||||||||||||
, dragonExperiencePoints :: Int | ||||||||||||||||||||||||||
, dragonTreasure :: Treasure | ||||||||||||||||||||||||||
, dragonFirePower :: Int | ||||||||||||||||||||||||||
, dragonHealth :: Int | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Comment on lines
+193
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a great idea! And almost the perfect solution 🔝 There're two problems with this particular data type definition:
myDragon = Dragon
{ dragonColor = Green
, dragonExperiencePoints = 50
, dragonTreasure = GoldPlusTreasure
, dragonFirePower = 70
, dragonHealth = 150
}
myDragon = Dragon
{ dragonColor = Red
, dragonExperiencePoints = 50
, dragonTreasure = Gold
, dragonFirePower = 70
, dragonHealth = 150
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chshersh You are right, I know I cut the corners a bit on this one :-) I enjoyed this particular exercise a lot as it was like a fantasy story with dragons, knights and treasures !! :-) I got the point about making invalid states unrepresentable, but could not make much progress towards it. I started watching the video you suggested, but wasn't able to watch it completely yesterday. I'm planning to go over it again. |
||||||||||||||||||||||||||
data FightOutcome | ||||||||||||||||||||||||||
= DragonDies | ||||||||||||||||||||||||||
| KnightDies | ||||||||||||||||||||||||||
| KnightRunsAway | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
dragonFight :: Knight -> Dragon -> FightOutcome | ||||||||||||||||||||||||||
dragonFight = go 0 | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
go :: Int -> Knight -> Dragon -> FightOutcome | ||||||||||||||||||||||||||
go n k d | ||||||||||||||||||||||||||
| knightHealth k <= 0 = KnightDies | ||||||||||||||||||||||||||
| dragonHealth d <= 0 = DragonDies | ||||||||||||||||||||||||||
| knightEndurance k == 0 = KnightRunsAway | ||||||||||||||||||||||||||
| otherwise = go n' k' d' | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
n' = n + 1 | ||||||||||||||||||||||||||
k' = k { knightEndurance = knightEndurance k - 1 | ||||||||||||||||||||||||||
, knightHealth = knightHealth k - if n' `mod` 10 == 0 then dragonFirePower d else 0 | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
d' = d { dragonHealth = dragonHealth d - knightAttack k } | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
---------------------------------------------------------------------------- | ||||||||||||||||||||||||||
-- Extra Challenges | ||||||||||||||||||||||||||
|
@@ -181,7 +237,11 @@ False | |||||||||||||||||||||||||
True | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
isIncreasing :: [Int] -> Bool | ||||||||||||||||||||||||||
isIncreasing = error "TODO" | ||||||||||||||||||||||||||
isIncreasing [] = True | ||||||||||||||||||||||||||
isIncreasing [_] = True | ||||||||||||||||||||||||||
isIncreasing (x : y : rest) | ||||||||||||||||||||||||||
| x > y = False | ||||||||||||||||||||||||||
| otherwise = isIncreasing (y : rest) | ||||||||||||||||||||||||||
Comment on lines
+242
to
+244
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can the implementation slightly more elegant by relying on laziness in Haskell 🦥
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Implement a function that takes two lists, sorted in the | ||||||||||||||||||||||||||
increasing order, and merges them into new list, also sorted in the | ||||||||||||||||||||||||||
|
@@ -194,7 +254,11 @@ verify that. | |||||||||||||||||||||||||
[1,2,3,4,7] | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
merge :: [Int] -> [Int] -> [Int] | ||||||||||||||||||||||||||
merge = error "TODO" | ||||||||||||||||||||||||||
merge xs [] = xs | ||||||||||||||||||||||||||
merge [] ys = ys | ||||||||||||||||||||||||||
merge (x : xs) (y : ys) | ||||||||||||||||||||||||||
| x < y = x : merge xs (y : ys) | ||||||||||||||||||||||||||
| otherwise = y : merge (x : xs) ys | ||||||||||||||||||||||||||
Comment on lines
+259
to
+261
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Absolutely correct solution! 💎 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, thanks! |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Implement the "Merge Sort" algorithm in Haskell. The @mergeSort@ | ||||||||||||||||||||||||||
function takes a list of numbers and returns a new list containing the | ||||||||||||||||||||||||||
|
@@ -211,8 +275,11 @@ The algorithm of merge sort is the following: | |||||||||||||||||||||||||
[1,2,3] | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
mergeSort :: [Int] -> [Int] | ||||||||||||||||||||||||||
mergeSort = error "TODO" | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
mergeSort [] = [] | ||||||||||||||||||||||||||
mergeSort [x] = [x] | ||||||||||||||||||||||||||
mergeSort xs = merge (sort ps) (sort qs) | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
(ps, qs) = splitAt (length xs `div` 2) xs | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use Additionally, you can avoid using In that case, you can implement a solution that traverses a list only once and splits its elements into two lists of elements on only even positions and only odd positions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I need to spend some more thought on this... |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Haskell is famous for being a superb language for implementing | ||||||||||||||||||||||||||
compilers and interpeters to other programming languages. In the next | ||||||||||||||||||||||||||
|
@@ -264,7 +331,16 @@ data EvalError | |||||||||||||||||||||||||
It returns either a successful evaluation result or an error. | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
eval :: Variables -> Expr -> Either EvalError Int | ||||||||||||||||||||||||||
eval = error "TODO" | ||||||||||||||||||||||||||
eval _ (Lit i) = Right i | ||||||||||||||||||||||||||
eval symbolTable (Var symbol) = case lookup symbol symbolTable of | ||||||||||||||||||||||||||
Nothing -> Left (VariableNotFound symbol) | ||||||||||||||||||||||||||
Just value -> Right value | ||||||||||||||||||||||||||
eval symbolTable (Add e1 e2) = addExpr (eval symbolTable e1) (eval symbolTable e2) | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
addExpr :: Either EvalError Int -> Either EvalError Int -> Either EvalError Int | ||||||||||||||||||||||||||
addExpr (Left x) _ = Left x | ||||||||||||||||||||||||||
addExpr _ (Left x) = Left x | ||||||||||||||||||||||||||
addExpr (Right x) (Right y) = Right (x + y) | ||||||||||||||||||||||||||
Comment on lines
+340
to
+343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a great helper function! 👏🏻 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
{- | Compilers also perform optimizations! One of the most common | ||||||||||||||||||||||||||
optimizations is "Constant Folding". It performs arithmetic operations | ||||||||||||||||||||||||||
|
@@ -288,4 +364,23 @@ Write a function that takes and expression and performs "Constant | |||||||||||||||||||||||||
Folding" optimization on the given expression. | ||||||||||||||||||||||||||
-} | ||||||||||||||||||||||||||
constantFolding :: Expr -> Expr | ||||||||||||||||||||||||||
constantFolding = error "TODO" | ||||||||||||||||||||||||||
constantFolding = addExpr . extractConstant | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
extractConstant :: Expr -> ([Int], [Expr]) | ||||||||||||||||||||||||||
extractConstant (Lit i) = ([i], []) | ||||||||||||||||||||||||||
extractConstant (Var x) = ([], [Var x]) | ||||||||||||||||||||||||||
extractConstant (Add x y) = (x1 ++ y1, x2 ++ y2) | ||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||
(x1, x2) = extractConstant x | ||||||||||||||||||||||||||
(y1, y2) = extractConstant y | ||||||||||||||||||||||||||
addExpr :: ([Int], [Expr]) -> Expr | ||||||||||||||||||||||||||
addExpr (c, e) = case (c, e) of | ||||||||||||||||||||||||||
([], []) -> error "invalid" | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting! You can see that it's actually impossible to have both lists empty at the same time. So here you're handling and impossible situation 🙂 Can you maybe change types a bit to avoid having this error? 😉 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chshersh :-) I know you mean "making invalid states unrepresentable", need to think about this some more ... |
||||||||||||||||||||||||||
(xs, []) -> Lit (sum xs) | ||||||||||||||||||||||||||
([], ys) -> addVars ys | ||||||||||||||||||||||||||
(xs, ys) -> if sum xs == 0 then addVars ys else Add (Lit (sum xs)) (addVars ys) | ||||||||||||||||||||||||||
addVars :: [Expr] -> Expr | ||||||||||||||||||||||||||
addVars [] = error "Invalid" | ||||||||||||||||||||||||||
addVars [x] = x | ||||||||||||||||||||||||||
addVars [x, y] = Add x y | ||||||||||||||||||||||||||
addVars (x : y : rest) = Add (Add x y) (addVars rest) | ||||||||||||||||||||||||||
Comment on lines
+380
to
+386
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can get rid of handling this impossible case too. In the pair you already pattern matched on the list, so you know that it's not empty. So yo can pass it's first element and the rest to the function and continue doing this recursively to avoid throwing
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @chshersh , will need to think about this some more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks to me that the first case is redundant because your helper function
go
will return1
immediately if the given list is empty.After you remove this case, you can also eta-reduce the top-level function ✂️