-
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?
Conversation
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.
Huge congratulations! You've completed the second homework 👏🏼
This homework has some rather difficult exercises but you managed to overcome these challenges! 🥳
Keep going! The first and the most difficult half of the course is done 💪
lazyProduct [] = 1 | ||
lazyProduct list = go 1 list |
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 return 1
immediately if the given list is empty.
After you remove this case, you can also eta-reduce the top-level function ✂️
lazyProduct [] = 1 | |
lazyProduct list = go 1 list | |
lazyProduct = go 1 |
go 0 _ = 0 | ||
go result [] = result | ||
go result (x : xs) = go (result * x) xs |
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.
That's a very clever solution! 🧠
I'm not sure if you know about BangPatterns or strict evaluations in Haskell (I'm going to cover them tomorrow in Lecture 3) but your solution also has the optimal performance behaviour 😅
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.
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 comment
The 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 comment
The 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 :smile
or :+1
.
headPart = take index list | ||
(element, tailPart) = case drop index list of |
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.
That's a correct implementation 👍🏻
Alternatively, you can use a standard function splitAt instead of using take
and drop
to avoid traversing the list prefix twice 🔃
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.
That's a good point.
@@ -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 comment
The 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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
There's no special magic 🙂
takeWhile
will just stop on the first space character and won't traverse the list any further. It works due to laziness.
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.
Got it, thanks!
|
||
data Treasure | ||
= Gold | ||
| GoldPlusTreasure |
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.
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 👍🏻
merge (x : xs) (y : ys) | ||
| x < y = x : merge xs (y : ys) | ||
| otherwise = y : merge (x : xs) ys |
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.
Absolutely correct solution! 💎
You can make it more performant by checking if both heads of the given lists are equal. In that case, you're able to reduce the size of each list by 1 on each recursive call instead of reducing the size of only a single list ✂️
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.
Got it, thanks!
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 comment
The reason will be displayed to describe this comment to others. Learn more.
You can use splitAt
instead of take + drop
to avoid traversing the first half twice.
Additionally, you can avoid using length
if you notice that you don't necessarily need to split in the middle. You only need to split it into two lists of approximately the same size.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I think I need to spend some more thought on this...
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) |
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.
That's a great helper function! 👏🏻
Once you learn about Monads, you'll be able to reduce this function to a single line ✂️
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.
Thanks!
(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 comment
The 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 comment
The 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 ...
([], 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) |
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.
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 error
([], 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) | |
([], y : ys) -> addVars y ys | |
(xs, ys) -> if sum xs == 0 then addVars ys else Add (Lit (sum xs)) (addVars ys) | |
addVars :: Expr -> [Expr] -> Expr | |
addVars e [] = e | |
addVars e (x : xs) = ... |
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.
Thanks @chshersh , will need to think about this some more.
Solutions for Lecture 2
cc @chshersh @vrom911