due at 23:59 on +80
Save all your functions into one file called a05.hs and submit it to this dropbox.
mapFirstRestDefine the function mapFirstRest, with this signature:
mapFirstRest :: (a -> b) -> (a -> b) -> [a] -> [b]It works like map, in that it applies a function to each element of a list. But unlike map it takes two separate function parameters, and applies the first function to the first list element, and the second function to the rest of the list. Here are some examples:
ghci> mapFirstRest (+5) (*5) [7..10]
[12,40,45,50]
ghci> mapFirstRest (+5) (*5) [8,8,8]
[13,40,40]
ghci> mapFirstRest length (const 0) ["Alice", "Bob", "Carol"]
[5,0,0]
ghci> mapFirstRest (+5) id [9,9,9]
[14,9,9]
ghci> mapFirstRest id (+5) [9,9,9]
[9,14,14]
In the above, we’re using id which is the identity function. It just returns its argument exactly as given:
ghci> id 9
9
ghci> id "Hello"
"Hello"
capitalizeCreate a function capitalize that will convert the first letter of its string argument to upper-case.
capitalize :: String -> StringTo do that, you should import Data.Char which contains a function toUpper that works on a single character:
ghci> import Data.Char
ghci> toUpper 'h'
'H'
ghci> toUpper 'G'
'G'
ghci> toUpper '%'
'%'
Use mapFirstRest to apply toUpper appropriately. Here are some sample inputs and outputs:
ghci> capitalize "hello"
"Hello"
ghci> capitalize "nifty"
"Nifty"
ghci> capitalize "NICE"
"NICE"
ghci> capitalize "#wow"
"#wow"
maybeCapitalize, titleCaseTitle case is the name for the capitalization rules typically used in titles and headlines, where every word is capitalized except articles (the, a), conjunctions (and, but, for), and prepositions (in, of). But the first word of the sentence is capitalized even if it is one of these words.
Define these in your solution:
exemptions :: [String]
maybeCapitalize :: String -> String
titleCase :: String -> StringThe exemptions should be a list of strings representing words that are exempt from capitalization in titles:
ghci> "the" `elem` exemptions
True
ghci> "dog" `elem` exemptions
False
The maybeCapitalize is a function that capitalizes only if the word is not exempt. If it is exempt, then it just returns the word unchanged. In your definition, you should use both exemptions and capitalize.
ghci> maybeCapitalize "the"
"the"
ghci> maybeCapitalize "dog"
"Dog"
Finally, titleCase is a function that takes a complete phrase, splits it into words, and applies capitalization appropriately. Your implementation should use capitalize, maybeCapitalize, mapFirstRest, words, unwords, and function composition.
The built-in functions words and unwords are great for disassembling a phrase into a list of words, and then putting it back together again:
ghci> words "the quick brown fox"
["the","quick","brown","fox"]
ghci> unwords ["jumps", "over", "the", "lazy", "dog."]
"jumps over the lazy dog."
Here are some examples for titleCase:
ghci> titleCase "the quick brown fox jumps over the lazy dog"
"The Quick Brown Fox Jumps Over the Lazy Dog"
ghci> titleCase "the hound of the baskervilles"
"The Hound of the Baskervilles"
ghci> titleCase "harry potter and the chamber of secrets"
"Harry Potter and the Chamber of Secrets"
Using the definition of Tree from the notes on 5 October, define traversals that concatenate together all the values in the specified order. Your functions will require that the type of values stored in the tree forms a monoid.
preorder :: Monoid a => Tree a -> a
inorder :: Monoid a => Tree a -> a
postorder :: Monoid a => Tree a -> aAt each node:
Here are traversals on the tree defined sample1 in the notes (and a reminder of what that tree contains).
ghci> printTree sample1
- "A"
|- "K"
| |- "M"
| | |- *
| | |- "Q"
| |- "P"
|- *
ghci> preorder sample1
"AKMQP"
ghci> inorder sample1
"MQKPA"
ghci> postorder sample1
"QMPKA"
Using the BoundedStack data type from the Assignment 4 solutions, define it as an instance of Monoid. Unlike the tree, it should not require that the element type is also a Monoid. Here is the first line of the instance definition:
instance Monoid (BoundedStack a) whereand then you add the definitions of mempty and mappend.
To append two stacks, you should combine together their capacities and place all the elements in the left stack before all the elements in the right stack. Here are some examples:
ghci> Just s1 = push 7 (new 3)
ghci> Just s2 = (push 8 >=> push 9) (new 2)
ghci> s1 <> s2
BoundedStack {capacity = 2, elements = [7,9,8]}
ghci> s2 <> mempty
BoundedStack {capacity = 0, elements = [9,8]}
ghci> mempty <> s1
BoundedStack {capacity = 2, elements = [7]}
Remember that <> is an operator shorthand for mappend.
import Data.Monoid
import Data.Char
import Control.Monad.State
main = do
flip execStateT (0,0) $ do
-- mapFirstRest
verify "1.01" [12,40,45,50] $ mapFirstRest (+5) (*5) [7..10]
verify "1.02" [13,40,40] $ mapFirstRest (+5) (*5) [8,8,8]
verify "1.03" [5,0,0] $ mapFirstRest length (const 0)
["Alice", "Bob", "Carol"]
verify "1.04" [14,9,9] $ mapFirstRest (+5) id [9,9,9]
verify "1.05" [9,14,14] $ mapFirstRest id (+5) [9,9,9]
-- capitalize
verify "2.01" "Hello" $ capitalize "hello"
verify "2.02" "Nifty" $ capitalize "nifty"
verify "2.03" "NICE" $ capitalize "NICE"
verify "2.04" "#wow" $ capitalize "#wow"
-- maybeCapitalize
assert "3.01" $ "the" `elem` exemptions
assert "3.02" $ not $ "dog" `elem` exemptions
verify "3.03" "the" $ maybeCapitalize "the"
verify "3.04" "Dog" $ maybeCapitalize "dog"
verify "3.05" "The Quick Brown Fox Jumps Over the Lazy Dog"
$ titleCase "the quick brown fox jumps over the lazy dog"
verify "3.06" "The Hound of the Baskervilles"
$ titleCase "the hound of the baskervilles"
verify "3.07" "Harry Potter and the Chamber of Secrets"
$ titleCase "harry potter and the chamber of secrets"
-- tree traversal
verify "4.01" "AKMQP" $ preorder sample1
verify "4.02" "MQKPA" $ inorder sample1
verify "4.03" "QMPKA" $ postorder sample1
verify "4.04" "BSCDFR" $ preorder sample2
verify "4.05" "SDCFBR" $ inorder sample2
verify "4.06" "DFCSRB" $ postorder sample2
-- stack monoid
let Just s1 = push 7 (new 3)
let Just s2 = (push 8 >=> push 9) (new 2)
verify "5.01" (BoundedStack {capacity = 2, elements = [7,9,8]})
$ s1 <> s2
verify "5.02" s2 $ s2 <> mempty
verify "5.03" s1 $ mempty <> s1
where
say = liftIO . putStrLn
correct (k, n) = (k+1, n+1)
incorrect (k, n) = (k, n+1)
assert s = verify s True
verify :: (Show a, Eq a) => String -> a -> a -> StateT (Int,Int) IO ()
verify = verify' (==)
verifyF = verify' (\x y -> abs(x-y) < 0.00001)
verify' :: (Show a) => (a -> a -> Bool) -> String -> a -> a ->
StateT (Int,Int) IO ()
verify' eq tag expected actual
| eq expected actual = do
modify correct
say $ " OK " ++ tag
| otherwise = do
modify incorrect
say $ "ERR " ++ tag ++ ": expected " ++ show expected
++ " got " ++ show actual
-- End of test driver