2. While filesystems on Unix are usually sensitive to case (e.g. “G” vs. “g”) in file names, Windows filesystems are not. Add a parameter to the globToRegex and matchesGlob functions that allows control over case sensitive matching.
Note that the solution below ignores ranges in character classes.
escape ::Char->Bool->String
escape c matchCase | c `elem` regexChars = '\' : [c] | matchCase =[c] |otherwise= '[' : nub (c:shiftCase c:[])++"]" where regexChars ="\\+()^$.{}]|"
charClass ::String->Bool->String
charClass (']':cs) matchCase = ']' : globToRegex' cs matchCase
charClass (c:cs) True = c : charClass cs True
charClass (c:cs) False = c' ++ charClass cs False where c' = nub (c:shiftCase c:[])
charClass []_=error"unterminated character class"
shiftCase ::Char->Char
shiftCase c | isLower c = toUpper c | isUpper c = toLower c |otherwise= c
matchesGlob :: FilePath ->String->Bool->Bool
matchesGlob name pat matchCase = name =~ globToRegex pat matchCase
Page 212:
1. Glob patterns are simple enough to interpret that it's easy to write a matcher directly in Haskell, rather than going through the regexp machinery. Give it a try.
Note: I forgot to implement negated character classes.
module MatchGlob (match)where
import Data.List (tails)
type Glob =String
match :: Glob ->String->Bool
match """"= True
match ""_= False
match "*"""= True
match _""= False
match (x:xs) z@(y:ys)= case x of
'?' -> match xs ys
'\' -> matchEscape xs z
'*' -> matchStar xs z
'[' -> matchClass xs z otherwise-> x == y && match xs ys
matchEscape :: Glob ->String->Bool
matchEscape []_=error"unescaped backslash"
matchEscape _""= False
matchEscape (x:xs)(y:ys)= x == y && match xs ys
matchStar :: Glob ->String->Bool
matchStar x y =or(map(match x)(tails y))
matchClass :: Glob ->String->Bool
matchClass (x:xs)(y:ys)= let(xs',zs)=break(== ']') xs inif zs =="" thenerror"unterminated character class" else y `elem` expand (x:xs')&& match (tail zs) ys
matchClass __=error"illegal character class"
2. Write a function splitWith that acts similarly to words but takes a predicate and a list of any type, and then splits its input list on every element for which the predicate returns False.
splitWith ::(a ->Bool)->[a]->[[a]]
splitWith p xs = let(pre,suf)=break p xs
suf' =dropWhile p suf in pre : case suf' of []->[] _-> splitWith p suf'
Page 97:
1. Use a fold (choosing the appropriate fold will make your code much simpler) to rewrite and improve upon the asInt function from the section called “Explicit recursion” on page 85.
asInt_fold ::String->Int
asInt_fold ('-':xs)=-1 * asInt_fold xs
asInt_fold xs =foldl' step 0 xs where
step ::Int->Char->Int
step x y =10* x + digitToInt y
6. Write your own definition of concat using foldr.
1. Write the converse of fromList for the List type: a function that takes a List a and generates a [a].
toList :: List a ->[a]
toList (Cons x y)= x : toList y
toList Nil =[]
2. Define a tree type that has only one constructor, like our Java example. Instead of the Empty constructor, use the Maybe type to refer to a node’s children.
data Tree a = Node a (Maybe(Tree a))(Maybe(Tree a)) deriving(Show)
Page 69:
1. Write a function that computes the number of elements in a list. To test it, ensure that it gives the same answers as the standard length function.
myLength []=0
myLength (x:xs)=1+ myLength xs
2. Add a type signature for your function to your source file. To test it, load the source file into ghci again.
3. Write a function that computes the mean of a list, i.e. the sum of all elements in the list divided by its length. (You may need to use the fromIntegral function to convert the length of the list from an integer into a floating point number.)
mean xs =sum xs /fromIntegral(length xs)
4. Turn a list into a palindrome, i.e. it should read the same both backwards and forwards. For example, given the list [1,2,3], your function should return [1,2,3,3,2,1].
palindrome xs = xs ++reverse xs
5. Write a function that determines whether its input list is a palindrome.
isPalindrome xs = xs ==reverse xs
6. Create a function that sorts a list of lists based on the length of each sublist. (You may want to look at the sortBy function from the Data.List module.)
Or, less bulky, using the on function from the Data.Function module.
sortByLength' = sortBy (compare `on` length)
7. Define a function that joins a list of lists together using a separator value.
intersperse :: a ->[[a]]->[a]
intersperse _[]=[]
intersperse _(xs:[])= xs
intersperse s (xs:xss)= xs ++[s]++(intersperse s xss)
8. Using the binary tree type that we defined earlier in this chapter, write a function that will determine the height of the tree. The height is the largest number of hops from the root to an Empty. For example, the tree Empty has height zero; Node "x" Empty Empty has height one; Node "x" Empty (Node "y" Empty Empty) has height two; and so on.
height :: Tree a ->Integer
height (Node _ x y)= 1 +max(height x)(height y)
height Empty =0
9. Consider three two-dimensional points a, b, and c. If we look at the angle formed by the line segment from a to b and the line segment from b to c, it either turns left, turns right, or forms a straight line. Define a Direction data type that lets you represent these possibilities.
data Direction = LeftTurn | RightTurn | Straight deriving(Show)
1. Haskell provides a standard function, last :: [a] -> a, that returns the last element of a list. From reading the type alone, what are the possible valid behaviors (omitting crashes and infinite loops) that this function could have?
From the type alone there is no way to find out what the real type is, neither is there a way to manipulate a value of that type, thus the “only” thing the code can do is return an element of the list.
What are a few things that this function clearly cannot do?
Return the size of the list.
Reverse the list.
2. Write a function, lastButOne, that returns the element before the last.
lastButOne = head . tail . reverse
Load your lastButOne function into ghci and try it out on lists of different lengths. What happens when you pass it a list that’s too short?
Prelude> lastButOne "a"
*** Exception: Prelude.head: empty list
Here are my answers to the exercises of chapter 1.
1. Enter the following expressions into ghci What are their types?
Prelude> 5 + 8
5 + 8
13
it :: Integer
Prelude> 3 * 5 + 8
3 * 5 + 8
23
it :: Integer
Prelude> 2 + 4
2 + 4
6
it :: Integer
Prelude> (+) 2 4
(+) 2 4
6
it :: Integer
Prelude> sqrt 16
sqrt 16
4.0
it :: Double
Prelude> succ 6
succ 6
7
it :: Integer
Prelude> succ 7
succ 7
8
it :: Integer
Prelude> pred 9
pred 9
8
it :: Integer
Prelude> pred 8
pred 8
7
it :: Integer
Prelude> sin (pi / 2)
sin (pi / 2)
1.0
it :: Double
Prelude> truncate pi
truncate pi
3
it :: Integer
Prelude> round 3.5
round 3.5
4
it :: Integer
Prelude> round 3.4
round 3.4
3
it :: Integer
Prelude> floor 3.7
floor 3.7
3
it :: Integer
Prelude> ceiling 3.3
ceiling 3.3
4
it :: Integer
2. From ghci, type :? to print some help. Define a variable and then type :show bindings. What do you see?
Prelude> let x = 1
let x = 1
Prelude> :show bindings
:show bindings
it :: Integer = 4
x :: Integer = _
Prelude> x
x
1
Prelude> :show bindings
:show bindings
it :: Integer = 4
x :: Integer = 1
3. The words function counts the number of words in a string. Modify the WC.hs example in order to count the number of words in a file.
main = interact wordCount
where wordCount input = show (length (words input)) ++ "n"
4. Modify the WC.hs example again, in order to print the number of characters in a file.
main = interact wordCount
where wordCount input = show (length input) ++ "n"