In this chapter, we encounter:
- conditional expressions
- guarded equations
- pattern matching (tuple, list, integer)
- lambda expressions
halve
Using library functions, define a function
halve :: [a] → ([a], [a])
that splits an even-lengthed list into two halves.For example:
> halve [1, 2, 3, 4, 5, 6]
([1, 2, 3], [4, 5, 6])
The function splitAt already provides the means to split one list from a given position. Here our position is the half of the length of the list, which gives:
halve :: [a] -> ([a], [a]) halve xs = splitAt (length xs `div` 2) xs
Executing in the repl, we obtain:
*Ch4> halve [1, 2, 3, 4, 5, 6] ([1,2,3],[4,5,6])
safetail
Consider a function
safetail :: [a] → [a]
that behaves as the library function tail, except that safetail maps the empty list to itself, whereas tail produces an error in this case.Define safetail using:
- a conditional expression
- guarded equations
- pattern matching
Hint: make use of the library function null.
conditional expression
We need the conditional to discriminate between the empty list (null xs
) and the non empty list.
If we are not in the empty list case, we can take the tail of list.
Thus giving:
safetail1 :: [a] -> [a] safetail1 xs = if null xs then [] else tail xs
Example:
*Ch4> safetail1 [] [] *Ch4> safetail1 [1,2,3] [2,3]
guarded equations
Same explanation as before:
safetail2 :: [a] -> [a] safetail2 xs | null xs = [] | otherwise = tail xs
Example:
*Ch4> safetail2 [] [] *Ch4> safetail2 [3,2,1] [2,1]
pattern matching
Using pattern matching is more intuitive (or so I thought). Either the list is empty and then we return it directly. Either, the list follows the pattern of having a head (which we do not care about, thus using the _) and a tail (thus returning the tail).
Here is the definition:
safetail3 :: [a] -> [a] safetail3 [] = [] safetail3 (_:xs) = xs
Example:
*Ch4> safetail3 [] [] *Ch4> safetail3 [1,3,5,7,11] [3,5,7,11]
Logical conjunction
In a similar way to ∧, show how the logical conjunction operator ∨ can be defined in four different ways using pattern matching.
1st
Naive one:
orr :: Bool -> Bool -> Bool orr False False = False orr True True = True orr True False = True orr False True = True
Check - Expected result: [False, True, True, True]
*Ch4> map (\ (f,s) -> orr f s) [(False, False), (False, True), (True, False), (True, True)] [False,True,True,True]
OK
2nd
orr2 :: Bool -> Bool -> Bool orr2 False False = False orr2 _ _ = True
Check - Expected result: [False, True, True, True]
*Ch4> map (\ (f,s) -> orr2 f s) [(False, False), (False, True), (True, False), (True, True)] [False,True,True,True]
OK
3d
orr3 :: Bool -> Bool -> Bool orr3 False b = b orr3 b False = b orr3 _ _ = True
Check - Expected result: [False, True, True, True]
*Ch4> map (\ (f,s) -> orr3 f s) [(False, False), (False, True), (True, False), (True, True)] [False,True,True,True]
OK
4th
orr4 :: Bool -> Bool -> Bool orr4 False b = b orr4 True _ = True
Check - Expected result: [False, True, True, True]
*Ch4> map (\ (f,s) -> orr4 f s) [(False, False), (False, True), (True, False), (True, True)] [False,True,True,True]
OK
Conjunction
Redefine the following version of the conjunction operator using conditional expressions rather than pattern matching:
True ∧ True = True
_ ∧ _ = False
and1 :: Bool -> Bool -> Bool and1 a b = if not a then False else if not b then False else True
Not quite readable.
conditional expression 2
Do the same for the following version, and note the difference in the number of conditional expressions required:
True ∧ b = b
False ∧ _ = False
and3 :: Bool -> Bool -> Bool and3 a b = if a then b else False
Curried function and lambda expression
Show how the curried function definition
mult x y z = x ∗ y ∗ z
can be understood in terms of lambda expressions.
Here it goes:
mult :: Int -> Int -> Int -> Int mult = (\ x -> \ y -> \ z -> x * y * z)
Executing in the haskell repl:
*Ch4> mult 10 20 30 6000