From 9288dfba6c673373f17c2ea6cfc6db3ce3ad59dc Mon Sep 17 00:00:00 2001 From: naprof007 Date: Fri, 24 Mar 2023 02:31:31 +0300 Subject: [PATCH 1/4] Chapter 1 Tasks --- src/Chapter1.hs | 69 +++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/Chapter1.hs b/src/Chapter1.hs index 406deeaca..a9518f08e 100644 --- a/src/Chapter1.hs +++ b/src/Chapter1.hs @@ -209,31 +209,31 @@ So, the output in this example means that 'False' has type 'Bool'. > Try to guess first and then compare your expectations with GHCi output >>> :t True - +True :: Bool >>> :t 'a' - +'a' :: Char >>> :t 42 - +42 :: Num a => a A pair of boolean and char: >>> :t (True, 'x') - +(True, 'x') :: (Bool, Char) Boolean negation: >>> :t not - +not :: Bool -> Bool Boolean 'and' operator: >>> :t (&&) - +(&&) :: Bool -> Bool -> Bool Addition of two numbers: >>> :t (+) - +(+) :: Num a => a -> a -> a Maximum of two values: >>> :t max - +max :: Ord a => a -> a -> a You might not understand each type at this moment, but don't worry! You've only started your Haskell journey. Types will become your friends soon. @@ -301,43 +301,43 @@ expressions in GHCi functions and operators first. Remember this from the previous task? ;) >>> 1 + 2 - +3 >>> 10 - 15 - +5 >>> 10 - (-5) -- negative constants require () - +15 >>> (3 + 5) < 10 - +True >>> True && False - +False >>> 10 < 20 || 20 < 5 - +True >>> 2 ^ 10 -- power - +1024 >>> not False - +True >>> div 20 3 -- integral division - +6 >>> mod 20 3 -- integral division remainder - +2 >>> max 4 10 - +10 >>> min 5 (max 1 2) - +2 >>> max (min 1 10) (min 5 7) - +5 Because Haskell is a __statically-typed__ language, you see an error each time you try to mix values of different types in situations where you are not @@ -429,6 +429,7 @@ task is to specify the type of this function. 49 -} +squareSum :: Num a => a -> a -> a squareSum x y = (x + y) * (x + y) @@ -449,7 +450,7 @@ Implement the function that takes an integer value and returns the next 'Int'. function body with the proper implementation. -} next :: Int -> Int -next x = error "next: not implemented!" +next x = x + 1 {- | After you've implemented the function (or even during the implementation), you @@ -490,7 +491,8 @@ Implement a function that returns the last digit of a given number. whether it works for you! -} -- DON'T FORGET TO SPECIFY THE TYPE IN HERE -lastDigit n = error "lastDigit: Not implemented!" +lastDigit :: Integral a => a -> a +lastDigit n = n `mod` 10 {- | @@ -520,7 +522,7 @@ branches because it is an expression and it must always return some value. satisfying the check will be returned and, therefore, evaluated. -} closestToZero :: Int -> Int -> Int -closestToZero x y = error "closestToZero: not implemented!" +closestToZero x y = if abs x < abs y then x else y {- | @@ -554,7 +556,11 @@ value after "=" where the condition is true. Casual reminder about adding top-level type signatures for all functions :) -} -mid x y z = error "mid: not implemented!" +mid :: Ord a => a -> a -> a -> a +mid x y z + | x < y && x > z || x > y && x < z = x + | y < x && y > z || y > x && y < z = y + | otherwise = z {- | =⚔️= Task 8 @@ -568,7 +574,10 @@ True >>> isVowel 'x' False -} -isVowel c = error "isVowel: not implemented!" +isVowel :: Char -> Bool +isVowel c + | c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' = True + | otherwise = False {- | @@ -632,7 +641,10 @@ Try to introduce variables in this task (either with let-in or where) to avoid specifying complex expressions. -} -sumLast2 n = error "sumLast2: Not implemented!" +sumLast2 :: Integral a => a -> a +sumLast2 n = b + a where + a = n `mod` 10 + b = (n `div` 10) `mod` 10 {- | @@ -653,7 +665,8 @@ You need to use recursion in this task. Feel free to return to it later, if you aren't ready for this boss yet! -} -firstDigit n = error "firstDigit: Not implemented!" +firstDigit :: Integral t => t -> t +firstDigit n = if n < 10 then n else firstDigit (n `div` 10) {- From cd869ed98799f8ee413a9988f0498b3805c2133a Mon Sep 17 00:00:00 2001 From: naprof007 Date: Fri, 24 Mar 2023 05:36:56 +0300 Subject: [PATCH 2/4] Chapter 1 Solutions fixes --- src/Chapter1.hs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Chapter1.hs b/src/Chapter1.hs index a9518f08e..a34fc7abd 100644 --- a/src/Chapter1.hs +++ b/src/Chapter1.hs @@ -304,7 +304,7 @@ expressions in GHCi 3 >>> 10 - 15 -5 +-5 >>> 10 - (-5) -- negative constants require () 15 @@ -492,7 +492,7 @@ Implement a function that returns the last digit of a given number. -} -- DON'T FORGET TO SPECIFY THE TYPE IN HERE lastDigit :: Integral a => a -> a -lastDigit n = n `mod` 10 +lastDigit n = abs n `mod` 10 {- | @@ -558,9 +558,9 @@ Casual reminder about adding top-level type signatures for all functions :) mid :: Ord a => a -> a -> a -> a mid x y z - | x < y && x > z || x > y && x < z = x - | y < x && y > z || y > x && y < z = y - | otherwise = z + | x <= y && x > z || x >= y && x < z = x + | y <= x && y > z || y >= x && y < z = y + | otherwise = z {- | =⚔️= Task 8 @@ -643,8 +643,9 @@ specifying complex expressions. sumLast2 :: Integral a => a -> a sumLast2 n = b + a where - a = n `mod` 10 - b = (n `div` 10) `mod` 10 + a = an `mod` 10 + b = (an `div` 10) `mod` 10 + an = abs n {- | @@ -666,7 +667,7 @@ aren't ready for this boss yet! -} firstDigit :: Integral t => t -> t -firstDigit n = if n < 10 then n else firstDigit (n `div` 10) +firstDigit n = let an = abs n in if an < 10 then an else firstDigit (an `div` 10) {- From 191c89f875fa599c972098eecf0dd7befbeda8cf Mon Sep 17 00:00:00 2001 From: naprof007 Date: Fri, 24 Mar 2023 07:17:37 +0300 Subject: [PATCH 3/4] Chapter 2 Solution --- src/Chapter2.hs | 66 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/src/Chapter2.hs b/src/Chapter2.hs index b98ceaf7d..f9b3d9f2a 100644 --- a/src/Chapter2.hs +++ b/src/Chapter2.hs @@ -136,42 +136,52 @@ functions in GHCi and insert the corresponding resulting output below: List of booleans: >>> :t [True, False] +[True, False] :: [Bool] String is a list of characters: >>> :t "some string" +"some string" :: String Empty list: >>> :t [] +[] :: [a] Append two lists: >>> :t (++) +(++) :: [a] -> [a] -> [a] Prepend an element at the beginning of a list: >>> :t (:) +(:) :: a -> [a] -> [a] Reverse a list: >>> :t reverse +reverse :: [a] -> [a] Take first N elements of a list: >>> :t take +take :: Int -> [a] -> [a] Create a list from N same elements: >>> :t replicate +replicate :: Int -> a -> [a] Split a string by line breaks: >>> :t lines +lines :: String -> [String] Join a list of strings with line breaks: >>> :t unlines +unlines :: [String] -> String -} @@ -186,30 +196,43 @@ Evaluate the following expressions in GHCi and insert the answers. Try to guess first, what you will see. >>> [10, 2] ++ [3, 1, 5] +[10,2,3,1,5] >>> [] ++ [1, 4] -- [] is an empty list +[1,4] >>> 3 : [1, 2] +[3,1,2] >>> 4 : 2 : [5, 10] -- prepend multiple elements +[4,2,5,10] >>> [1 .. 10] -- list ranges +[1,2,3,4,5,6,7,8,9,10] >>> [10 .. 1] +[] >>> [10, 9 .. 1] -- backwards list with explicit step +[10,9,8,7,6,5,4,3,2,1] >>> length [4, 10, 5] -- list length +3 >>> replicate 5 True +[True,True,True,True,True] >>> take 5 "Hello, World!" +"Hello" >>> drop 5 "Hello, World!" +", World!" >>> zip "abc" [1, 2, 3] -- convert two lists to a single list of pairs +[('a',1),('b',2),('c',3)] >>> words "Hello Haskell World!" -- split the string into the list of words +["Hello","Haskell","World!"] @@ -336,7 +359,9 @@ from it! ghci> :l src/Chapter2.hs -} subList :: Int -> Int -> [a] -> [a] -subList = error "subList: Not implemented!" +subList a b xs + | a < 0 || b < 0 = [] + | otherwise = take (b - a + 1) (drop a xs) {- | =⚔️= Task 4 @@ -349,7 +374,8 @@ Implement a function that returns only the first half of a given list. "b" -} -- PUT THE FUNCTION TYPE IN HERE -firstHalf l = error "firstHalf: Not implemented!" +firstHalf :: [a] -> [a] +firstHalf l = take (length l `div` 2) l {- | @@ -501,7 +527,9 @@ True >>> isThird42 [42, 42, 0, 42] False -} -isThird42 = error "isThird42: Not implemented!" +isThird42 :: (Eq a, Num a) => [a] -> Bool +isThird42 (_ : _ : 42 : _) = True +isThird42 _ = False {- | @@ -606,7 +634,8 @@ Implement a function that duplicates each element of the list -} duplicate :: [a] -> [a] -duplicate = error "duplicate: Not implemented!" +duplicate [] = [] +duplicate (x:xs) = x : x : duplicate xs {- | @@ -621,7 +650,10 @@ Write a function that takes elements of a list only in even positions. >>> takeEven [2, 1, 3, 5, 4] [2,3,4] -} -takeEven = error "takeEven: Not implemented!" +takeEven :: [a] -> [a] +takeEven [] = [] +takeEven [x] = [x] +takeEven (x:_:xs) = x : takeEven xs {- | =🛡= Higher-order functions @@ -728,7 +760,7 @@ value of the element itself 🕯 HINT: Use combination of 'map' and 'replicate' -} smartReplicate :: [Int] -> [Int] -smartReplicate l = error "smartReplicate: Not implemented!" +smartReplicate l = concat (map (\x -> replicate x x) l) {- | =⚔️= Task 9 @@ -741,7 +773,8 @@ the list with only those lists that contain a passed element. 🕯 HINT: Use the 'elem' function to check whether an element belongs to a list -} -contains = error "contains: Not implemented!" +contains :: (Foldable t, Eq a) => a -> [t a] -> [t a] +contains n = filter (n `elem`) {- | @@ -781,13 +814,15 @@ Let's now try to eta-reduce some of the functions and ensure that we mastered the skill of eta-reducing. -} divideTenBy :: Int -> Int -divideTenBy x = div 10 x +divideTenBy = div 10 -- TODO: type ;) -listElementsLessThan x l = filter (< x) l +listElementsLessThan :: Ord a => a -> [a] -> [a] +listElementsLessThan x = filter (< x) -- Can you eta-reduce this one??? -pairMul xs ys = zipWith (*) xs ys +pairMul :: Num c => [c] -> [c] -> [c] +pairMul = zipWith (*) {- | =🛡= Lazy evaluation @@ -842,7 +877,10 @@ list. 🕯 HINT: Use the 'cycle' function -} -rotate = error "rotate: Not implemented!" +rotate :: Int -> [a] -> [a] +rotate n l + | n < 0 = [] + | otherwise = take (length l) (drop n (cycle l)) {- | =💣= Task 12* @@ -858,7 +896,11 @@ and reverses it. function, but in this task, you need to implement it manually. No cheating! -} -rewind = error "rewind: Not Implemented!" +rewind :: [a] -> [a] +rewind [] = [] +rewind l = rewind' [] l where + rewind' reversed [] = reversed + rewind' reversed (x:xs) = rewind' (x : reversed) xs {- From b014b14e541c1b921112fc92607010a464777f0f Mon Sep 17 00:00:00 2001 From: naprof007 Date: Sat, 25 Mar 2023 03:54:46 +0300 Subject: [PATCH 4/4] Chapter 4 Solutions --- src/Chapter4.hs | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/Chapter4.hs b/src/Chapter4.hs index caec5a95d..0c4f7b59b 100644 --- a/src/Chapter4.hs +++ b/src/Chapter4.hs @@ -113,22 +113,30 @@ As always, try to guess the output first! And don't forget to insert the output in here: >>> :k Char +Char :: * >>> :k Bool +Bool :: * >>> :k [Int] +[Int] :: * >>> :k [] +[] :: * -> * >>> :k (->) +(->) :: * -> * -> * >>> :k Either +Either :: * -> * -> * >>> data Trinity a b c = MkTrinity a b c >>> :k Trinity +Trinity :: * -> * -> * -> * >>> data IntBox f = MkIntBox (f Int) >>> :k IntBox +IntBox :: (* -> *) -> * -} @@ -292,7 +300,8 @@ values and apply them to the type level? -} instance Functor (Secret e) where fmap :: (a -> b) -> Secret e a -> Secret e b - fmap = error "fmap for Box: not implemented!" + fmap _ (Trap e) = Trap e + fmap f (Reward x) = Reward (f x) {- | =⚔️= Task 3 @@ -306,6 +315,10 @@ data List a = Empty | Cons a (List a) +instance Functor List where + fmap _ Empty = Empty + fmap f (Cons x xs) = Cons (f x) (fmap f xs) + {- | =🛡= Applicative @@ -471,10 +484,12 @@ Implement the Applicative instance for our 'Secret' data type from before. -} instance Applicative (Secret e) where pure :: a -> Secret e a - pure = error "pure Secret: Not implemented!" + pure = Reward (<*>) :: Secret e (a -> b) -> Secret e a -> Secret e b - (<*>) = error "(<*>) Secret: Not implemented!" + (Trap f) <*> _ = Trap f + _ <*> (Trap x) = Trap x + Reward f <*> x = fmap f x {- | =⚔️= Task 5 @@ -489,6 +504,16 @@ Implement the 'Applicative' instance for our 'List' type. -} +instance Applicative List where + pure x = Cons x Empty + (<*>) :: List (a -> b) -> List a -> List b + Empty <*> _ = Empty + _ <*> Empty = Empty + Cons f fs <*> x = fmap f x `concatList` (fs <*> x) where + concatList Empty xs = xs + concatList xs Empty = xs + concatList xs (Cons y ys) = concatList (Cons y xs) ys + {- | =🛡= Monad @@ -599,7 +624,8 @@ Implement the 'Monad' instance for our 'Secret' type. -} instance Monad (Secret e) where (>>=) :: Secret e a -> (a -> Secret e b) -> Secret e b - (>>=) = error "bind Secret: Not implemented!" + Trap x >>= _ = Trap x + Reward x >>= f = f x {- | =⚔️= Task 7 @@ -610,6 +636,13 @@ Implement the 'Monad' instance for our lists. maybe a few) to flatten lists of lists to a single list. -} +instance Monad List where + Empty >>= _ = Empty + Cons x xs >>= f = f x `concatList` (Cons x xs >>= f) where + concatList Empty xs = xs + concatList xs Empty = xs + concatList xs (Cons y ys) = concatList (Cons y xs) ys + {- | =💣= Task 8*: Before the Final Boss @@ -628,7 +661,7 @@ Can you implement a monad version of AND, polymorphic over any monad? 🕯 HINT: Use "(>>=)", "pure" and anonymous function -} andM :: (Monad m) => m Bool -> m Bool -> m Bool -andM = error "andM: Not implemented!" +andM mx my = mx >>= \x -> my >>= \y -> pure $ x && y {- | =🐉= Task 9*: Final Dungeon Boss