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 ∗ zcan 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
