Haskell Basics
Question 1
-
17.. -
23.. Note that *has higher precedence than+. -
1. Exponentiation has a higher precedence than modulo (non-operator functions likemodthat are called in an infix manner can have a well-defined operator precedence level). -
24.25. Regular division of integers gives aFractionaltype. -
24. Thedivfunction is similar to//in Python. -
1. First we evaluate the conditionlet x = 3 in x + 3evaluates to3 + 3which therefore is6. Clearly6 /= 5is true, so we need to also evaluate3 < 4, which is also true.&&is the same asandin Python, soTrue and Trueis thereforeTrue. Thus, the whole expression evaluates to the if branch, which is1. -
False.otherwiseis actually justTrueby definition, sonot TruebecomesFalse. -
It actually causes a compile-time error since it is a type error.
fstandsndreceive pairs, so these functions do not work on triples. -
1.5. Thesuccfunction returns the successor of any enumerable type. For numbers, this would be one more than the number. -
1.4142135623730951. Straightforward. Notice that Haskell’s Prelude (the built-in stuff) comes with many math functions. -
True. Theelemfunction is similar toinin Python. -
4. When writingletbindings in a single line, we can separate multiple definitions with;. Therefore, we have defined two functionsfandgwhich add one and multiply by 2 respectively. The.operator is function composition, where, so (g . f) 1is the same asg (f 1), which evaluates to4. -
[1, 2, 3, 4, 5, 6]. This is straightforward, since++concatenates two lists. -
1.headreturns the first element of the list. -
[2, 3].tailreturns the suffix of the list without the first element. -
[1, 2].initreturns the list without the last element. -
1.!!is indexing. -
True.nullchecks whether a list is empty. -
3. Obvious. -
[3].drop ndrops the firstnelements of a list. -
[-1, 0, 1, 2, 3].take ntakes the firstnelements of a list. The range[-1..]is an infinitely long range from-1to infinity. -
[5, 1, 2 ,3].dropWhile fwill drop elements from a list untilfreturns false for an element. -
30. The easiest way to see this is by converting this to the equivalent Python expression:Python interactive session >>> sum([x[0] for x in[(i, j) for i in range(1, 5)for j in range(-1, 2)]])Going back to Haskell land, let us evaluate the inner list first.
[(i, j) | i <- [1..4], j <- [-1..1]]gives[(1, -1), (1, 0), (1, 1), (2, -1), ..., (4, 1)]then,[fst x | x <- ...]would therefore give[1,1,1,2,2,2,3,3,3,4,4,4]which sums to 30.
Question 2
Idea: take the last elements of both lists, and check for
equality. For this, we can use the last function.
eqLast xs ys = last xs == last ysQuestion 3
Idea: reverse the string, and check if the string and its
reverse are equal. For this, we can use the reverse
function.
isPalindrome w = w == reverse wQuestion 4
taxiFare f r d = f + r * dQuestion 5
There are several ways to approach this problem. Let us
first define the ingredientPrice function which should be
straightforward to do.
ingredientPrice i | i == 'B' = 0.5 | i == 'C' = 0.8 | i == 'P' = 1.5 | i == 'V' = 0.7 | i == 'O' = 0.4 | i == 'M' = 0.9Then we can define burgerPrice recursively. If the string is
empty then the price is 0. Otherwise, take the price of the first
ingredient and add that to the price of the remaining burger.
burgerPrice burger | null burger = 0 | otherwise = let first = ingredientPrice (head burger) rest = burgerPrice (tail burger) in first + restOf course, we know that we can do the following in Python quite nicely:
def burger_price(burger): return sum(ingredient_price(i) for i in burger)This can be done in Haskell too as follows:
burgerPrice burger = sum [ingredientPrice i | i <- burger]We can also replace the comprehension expression in Python using
map:
def burger_price(burger): return sum(map(ingredient_price, burger))Haskell also has a map (or fmap) function that
does the same thing:
burgerPrice burger = sum $ map ingredientPrice burgerThe $ sign is just regular function application, except that $ binds
very weakly. So sum $ map ingredientPrice burger is basically
sum (map ingredientPrice burger).
Finally, notice that
burgerPrice x = sum ((map ingredientPrice) x), so
effectively we can finally define our function this way:
burgerPrice = sum . map ingredientPrice where ingredientPrice i | i == 'B' = 0.5 | i == 'C' = 0.8 | i == 'P' = 1.5 | i == 'V' = 0.7 | i == 'O' = 0.4 | i == 'M' = 0.9To see this, let burgerPrice, sum and map ingredientPrice. We have shown that
By definition,
This style of writing functions is known as point-free style, where functions are expressed as a composition of functions.
Question 6
Again, there are several ways to solve this. To do so numerically, we can define our function recursively:
sumDigits n | n < 10 = n | otherwise = n `mod` 10 + sumDigits (n `div` 10)Alternatively, we may convert n into a string, convert each character
into integers, then obtain the sum. This might be expressed in Python
as:
def sum_digits(n): return sum(map(int, str(n)))Converting n into a string can be done by show:
ghci> show 123"123"Converting back into an integer can be done with read (you have to
explicitly state the output type of the read function since this can
be ambiguous):
ghci> read "123" :: Int123However, we can’t read from characters since the read function
receives strings. Good thing that strings are lists of characters, so by
putting the character in a list, we now obtain the ability to read a
digit (as a character) as an integer.
ghci> read '1' :: Int-- error!ghci> read ['1'] :: Int1To put things into lists, we can use the return function!
ghci> return '1' :: String"1"ghci> (read . return) '1' :: Int1Thus, the read . return function allows us to parse each character
into an integer. Combining this with what we had before, we can obtain
the list of the digits (as integers) from n using:
ghci> [(read . return) digit | digit <- show 123] :: [Int][1, 2, 3]Again, we can use map instead of list comprehension.
ghci> map (read . return) (show 123) :: [Int][1, 2, 3]Obtaining the sum of this list gives us exactly what we want. Thus, our
sumDigits function is succinctly defined as follows:
sumDigits = sum . map (read . return) . showQuestion 7
Idea: drop the first start elements, then take the
stop - start elements after that.
ls @: (start, stop) = take (stop - start) (drop start ls)