Skip to content
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

Complete chapter 2 #534

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 56 additions & 27 deletions src/Chapter2.hs
Original file line number Diff line number Diff line change
Expand Up @@ -136,43 +136,43 @@ 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" :: [Char]

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

-}

Expand All @@ -186,32 +186,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!"]

👩‍🔬 Haskell has a lot of syntax sugar. In the case with lists, any
list literal like "[3, 1, 2]" is syntax sugar for prepending elements
Expand Down Expand Up @@ -336,7 +347,8 @@ from it!
ghci> :l src/Chapter2.hs
-}
subList :: Int -> Int -> [a] -> [a]
subList = error "subList: Not implemented!"
subList a b xs = if a > b then [] else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your implementation is correct when the right bound is lower than the left. But I think you also check on negative bounds here too 👌🏼

drop a . take (b + 1) $ xs

{- |
=⚔️= Task 4
Expand All @@ -348,9 +360,10 @@ Implement a function that returns only the first half of a given list.
>>> firstHalf "bca"
"b"
-}
-- PUT THE FUNCTION TYPE IN HERE
firstHalf l = error "firstHalf: Not implemented!"

firstHalf :: [a] -> [a]
firstHalf l = take len l
where len = (length l) `div` 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat usage of where! Note that brackets are not needed when you use the function in the infix form 🙂

Suggested change
where len = (length l) `div` 2
where len = length l `div` 2


{- |
=🛡= Pattern matching
Expand Down Expand Up @@ -501,7 +514,10 @@ True
>>> isThird42 [42, 42, 0, 42]
False
-}
isThird42 = error "isThird42: Not implemented!"

isThird42 :: Integral a => [a] -> Bool
isThird42 (_ : _ : x : _) = x == 42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to compare x with 42 separately, you can pattern match on 42 directly in the pattern.

Suggested change
isThird42 (_ : _ : x : _) = x == 42
isThird42 (_ : _ : 42 : _) = True

isThird42 _ = False


{- |
Expand Down Expand Up @@ -605,9 +621,10 @@ Implement a function that duplicates each element of the list
"aabbaacc"

-}
duplicate :: [a] -> [a]
duplicate = error "duplicate: Not implemented!"
-- Couldn't figure out how to do it with a pattern match? Not sure if this is right

duplicate :: [a] -> [a]
duplicate = concatMap (replicate 2)

{- |
=⚔️= Task 7
Expand All @@ -621,7 +638,11 @@ 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 (x : y ) = x : go y
where go (y:x) = takeEven x
go [] = []
takeEven [] = []

{- |
=🛡= Higher-order functions
Expand Down Expand Up @@ -727,8 +748,9 @@ value of the element itself

🕯 HINT: Use combination of 'map' and 'replicate'
-}

smartReplicate :: [Int] -> [Int]
smartReplicate l = error "smartReplicate: Not implemented!"
smartReplicate l = concatMap (\x -> replicate x x) l
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, after you've mastered eta-reduction, you can apply such a technique to this function as well 🙂
But your implementation is already great 💯


{- |
=⚔️= Task 9
Expand All @@ -741,7 +763,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 :: Integral a => a -> [[a]] -> [[a]]
contains n xs = filter (\ys -> n `elem` ys) xs


{- |
Expand Down Expand Up @@ -780,14 +803,16 @@ nextInt = add 1
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 :: (Num a, Ord a) => a -> [a] -> [a]
listElementsLessThan x = filter (< x)

-- Can you eta-reduce this one???
pairMul xs ys = zipWith (*) xs ys
pairMul :: Num a => [a] -> [a] -> [a]
pairMul xs = (zipWith (*) xs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can go even further and eta reduce xs as well:

Suggested change
pairMul xs = (zipWith (*) xs)
pairMul = zipWith (*)


{- |
=🛡= Lazy evaluation
Expand Down Expand Up @@ -842,7 +867,9 @@ list.

🕯 HINT: Use the 'cycle' function
-}
rotate = error "rotate: Not implemented!"
rotate n xs = if (n /= (abs n)) then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a check on negative number?
In that case, you can be more explicit about it, I think 🙂

[]
else take (length xs) $ drop n $ (cycle xs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, cycle fails in runtime on empty lists ♻️ So you need to handle this case separately for this function to work properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, you can optimise the solution by dropping only mod n (length of the list) 🚤


{- |
=💣= Task 12*
Expand All @@ -858,8 +885,10 @@ 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 (x:xs) = rewind xs ++ [x]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your solution is correct! However, it is slow. In lists, it is quite slow to add anything at the end of the list. That is why it is always better to rewrite it with the : cons. Remember the explanation with trains? 🚂 🚋 🚋

That is why a more efficient solution is with the accumulator and the recursive function that will do the addition at the start of the list which is instant!

You can read a bit more about the go pattern in here: https://kowainik.github.io/posts/haskell-mini-patterns#recursive-go

rewind [] = []

{-
You did it! Now it is time to open pull request with your changes
Expand Down