RE-EXAM
Introduction to Functional Programming
TDA555/DIT440



DAY: January 3, 2015 TIME: 08:30–12:30 PLACE: V-salar


Responsible:

Aids:

Grade:

Emil Axelsson, D&IT, Tel: 0733-701736

An English (or English-Swedish, or English-X) dictionary

Completing Part I gives a 3 or a G;
Part I and Part II are both needed for a 4, 5, or VG


This exam consists of two parts:
Part I   (5 small assignments)

  • Give good enough answers for 4 assignments here and you will get a 3 or a G
Part II   (2 larger assignments)

  • You do not need to solve this part if you are happy with a 3 or a G!
  • Do Part I and one assignment of your choice here and you will get a 4
  • Do Part I and both assignments here and you will get a 5 or a VG


Please read the following guidelines carefully:
  • Answers can be given in Swedish or English
  • Begin each assignment on a new sheet
  • Write your number on each sheet
  • Write clearly; unreadable = wrong!
  • You can make use of the standard Haskell functions and types given in the attached list (you have to implement other functions yourself if you want to use them)
  • You do not have to import standard modules in your solutions

  • Good Luck!

    Part I

    You have to complete 4 out of the following 5 assignments to get a pass on the exam.


    1. Implement a function

    occurs :: Eq a => a -> [a] -> Int

    that given an x and a list xs, finds out how often x occurs in the list.

    Examples:

    *Main> occurs 'a' "bepacepadepa"
    3
    
    *Main> occurs 11 [2,3,5,7,11,13,7,5]
    1
    
    *Main> occurs "hej" ["hi","hola","hello","hoi"]
    0

    2. Implement a function

    urls :: String -> [String]

    that given a text (as a string), returns a list with all web addresses (URLs) in that text. We say that web addresses are words that start with the string “http://”.

    Examples:

    *Main> urls "I tried loading http://www.chalmers.se and http://www.google.com but I didn't succeed."
    ["http://www.chalmers.se","http://www.google.com"]
    
    *Main> urls "It's fun to stay at the Y.M.C.A."
    []

    Hint:


    3. Consider the following recursive data type which represents roads:

    data Road
      = City String
      | Fork Road Road

    City c represents a road that goes straight to the city c.

    Fork l r represents a road on which turning left leads to another road l and turning right leads to the road r.

    Here is an example road and the corresponding map:

    middleOfNowhere :: Road
    middleOfNowhere =
        Fork
          ( City "Mora" )
          ( Fork
              ( Fork
                  ( City "Kiruna" )
                  ( City "Gävle" )
              )
              ( City "Stockholm" )
          )

    Now, implement a function

    reachable :: String -> Road -> Bool

    that checks whether a given city is reachable from the given road.

    Examples:

    *Main> reachable "Kiruna" middleOfNowhere
    True
    
    *Main> reachable "Jukkasjärvi" middleOfNowhere
    False

    Hints:


    4. Write a QuickCheck property that expresses the fact that nub never returns a list that is longer than its argument.


    5. Write a function

    copyFile :: FilePath -> FilePath -> IO ()

    which takes two file names, and copies the contents of the first file to the second file.

    Example:

    *Main> readFile "apa.txt"
    "abracadabra"
    
    *Main> copyFile "apa.txt" "bepa.txt"
    
    *Main> readFile "bepa.txt"
    "abracadabra"

    Hint:


    6. Herman is implementing a car navigation system for Android devices. As the internal representation of maps, he decided to use the Road data type from assignment 3. One part of the system’s functionality is to be able to present a list of directions given a current position and a destination. The car’s current position is represented as a value of type Road, and the result of the navigation system is a list of “turns”, where a turn is either left or right:

    data Turn = L | R deriving (Show)

    Implement the function roadMap which generates the list of directions to reach a given city:

    roadMap :: String -> Road -> Maybe [Turn]

    A call to this function takes the form roadMap city pos, where city is the destination city and pos is the car’s current position.

    Examples:

    *Main> roadMap "Stockholm" middleOfNowhere
    Just [R,R]
    
    *Main> roadMap "Kiruna" middleOfNowhere
    Just [R,L,L]
    
    *Main> roadMap "Tanum" middleOfNowhere
    Nothing

    7. In this assignment, you will implement a text processing function called fill that fills paragraphs of text. Your function will take two arguments: (1) an integer indicating the line width of the text, and (2) a list of words. It should then produce a list of lines (the text) containing all the words in the right order, but not exceeding the specified line width.

    The type of the function is thus:

    fill :: Int -> [String] -> [String]
    --      width  words       lines

    Here are two examples of its use, demonstrating the effect of line widths 35 and 50 on the same text (taken from the Wikipedia entry on cats):

    *Main> do s <- readFile "wp_cats.txt"; putStr (unlines (fill 35 (words s)))
    The cat (Felis catus), also known
    as the domestic cat or housecat to
    distinguish it from other felids
    and felines, is a small, usually
    furry, domesticated, carnivorous
    mammal that is valued by humans for
    its companionship and for its
    ability to hunt vermin and
    household pests.
    
    *Main> do s <- readFile "wp_cats.txt"; putStr (unlines (fill 50 (words s)))
    The cat (Felis catus), also known as the domestic
    cat or housecat to distinguish it from other
    felids and felines, is a small, usually furry,
    domesticated, carnivorous mammal that is valued by
    humans for its companionship and for its ability
    to hunt vermin and household pests.

    Hint:

    Appendix

    
    {-
    This is a list of selected functions from the standard Haskell modules:
    
      Prelude
      Data.List
      Data.Maybe
      Data.Char
    -}
    
    --------------------------------------------------------------------------
    -- standard type classes
    
    class Show a where
      show :: a -> String
    
    class Eq a where
      (==), (/=) :: a -> a -> Bool
    
    class (Eq a) => Ord a where
      (<), (<=), (>=), (>) :: a -> a -> Bool
      max, min             :: a -> a -> a
    
    class (Eq a, Show a) => Num a where
      (+), (-), (*)    :: a -> a -> a
      negate           :: a -> a
      abs, signum      :: a -> a
      fromInteger      :: Integer -> a
    
    class (Num a, Ord a) => Real a where
      toRational       ::  a -> Rational
    
    class (Real a, Enum a) => Integral a where
      quot, rem        :: a -> a -> a
      div, mod         :: a -> a -> a
      toInteger        :: a -> Integer
    
    class (Num a) => Fractional a where
      (/)              :: a -> a -> a
      fromRational     :: Rational -> a
    
    class (Fractional a) => Floating a where
      exp, log, sqrt      :: a -> a
      sin, cos, tan       :: a -> a
    
    class (Real a, Fractional a) => RealFrac a where
      truncate, round  :: (Integral b) => a -> b
      ceiling, floor   :: (Integral b) => a -> b
    
    --------------------------------------------------------------------------
    -- numerical functions
    
    even, odd        :: (Integral a) => a -> Bool
    even n           = n `rem` 2 == 0
    odd              = not . even
    
    --------------------------------------------------------------------------
    -- monadic functions
    
    sequence     :: Monad m => [m a] -> m [a]
    sequence     = foldr mcons (return [])
                        where mcons p q = do x <- p; xs <- q; return (x:xs)
    
    sequence_    :: Monad m => [m a] -> m ()
    sequence_ xs = do sequence xs; return ()
    
    --------------------------------------------------------------------------
    -- functions on functions
    
    id               :: a -> a
    id x             = x
    
    const            :: a -> b -> a
    const x _        = x
    
    (.)              :: (b -> c) -> (a -> b) -> a -> c
    f . g            = \ x -> f (g x)
    
    flip             :: (a -> b -> c) -> b -> a -> c
    flip f x y       = f y x
    
    ($) :: (a -> b) -> a -> b
    f $  x           = f x
    
    --------------------------------------------------------------------------
    -- functions on Bools
    
    data Bool = False | True
    
    (&&), (||)       :: Bool -> Bool -> Bool
    True  && x       = x
    False && _       = False
    True  || _       = True
    False || x       = x
    
    not              :: Bool -> Bool
    not True         = False
    not False        = True
    
    --------------------------------------------------------------------------
    -- functions on Maybe
    
    data Maybe a = Nothing | Just a
    
    isJust                 :: Maybe a -> Bool
    isJust (Just a)        =  True
    isJust Nothing         =  False
    
    isNothing              :: Maybe a -> Bool
    isNothing              =  not . isJust
    
    fromJust               :: Maybe a -> a
    fromJust (Just a)      =  a
    
    maybeToList            :: Maybe a -> [a]
    maybeToList Nothing    =  []
    maybeToList (Just a)   =  [a]
    
    listToMaybe            :: [a] -> Maybe a
    listToMaybe []         =  Nothing
    listToMaybe (a:_)      =  Just a
    
    --------------------------------------------------------------------------
    -- functions on pairs
    
    fst              :: (a,b) -> a
    fst (x,y)        =  x
    
    snd              :: (a,b) -> b
    snd (x,y)        =  y
    
    curry            :: ((a, b) -> c) -> a -> b -> c
    curry f x y      =  f (x, y)
    
    uncurry          :: (a -> b -> c) -> ((a, b) -> c)
    uncurry f p      =  f (fst p) (snd p)
    
    --------------------------------------------------------------------------
    -- functions on lists
    
    map :: (a -> b) -> [a] -> [b]
    map f xs = [ f x | x <- xs ]
    
    (++) :: [a] -> [a] -> [a]
    xs ++ ys = foldr (:) ys xs
    
    filter :: (a -> Bool) -> [a] -> [a]
    filter p xs = [ x | x <- xs, p x ]
    
    concat :: [[a]] -> [a]
    concat xss = foldr (++) [] xss
    
    concatMap :: (a -> [b]) -> [a] -> [b]
    concatMap f = concat . map f
    
    head, last       :: [a] -> a
    head (x:_)       = x
    
    last [x]         = x
    last (_:xs)      = last xs
    
    tail, init       :: [a] -> [a]
    tail (_:xs)      = xs
    
    init [x]         = []
    init (x:xs)      = x : init xs
    
    null             :: [a] -> Bool
    null []          = True
    null (_:_)       = False
    
    length           :: [a] -> Int
    length []        = 0
    length (_:l)     = 1 + length l
    
    (!!)             :: [a] -> Int -> a
    (x:_)  !! 0      = x
    (_:xs) !! n      = xs !! (n-1)
    
    foldr            :: (a -> b -> b) -> b -> [a] -> b
    foldr f z []     =  z
    foldr f z (x:xs) =  f x (foldr f z xs)
    
    foldl            :: (a -> b -> a) -> a -> [b] -> a
    foldl f z []     =  z
    foldl f z (x:xs) =  foldl f (f z x) xs
    
    iterate          :: (a -> a) -> a -> [a]
    iterate f x      =  x : iterate f (f x)
    
    repeat           :: a -> [a]
    repeat x         =  xs where xs = x:xs
    
    replicate        :: Int -> a -> [a]
    replicate n x    =  take n (repeat x)
    
    cycle            :: [a] -> [a]
    cycle []         =  error "Prelude.cycle: empty list"
    cycle xs         =  xs' where xs' = xs ++ xs'
    
    take, drop             :: Int -> [a] -> [a]
    take n _      | n <= 0 =  []
    take _ []              =  []
    take n (x:xs)          =  x : take (n-1) xs
    
    drop n xs     | n <= 0 =  xs
    drop _ []              =  []
    drop n (_:xs)          =  drop (n-1) xs
    
    splitAt                :: Int -> [a] -> ([a],[a])
    splitAt n xs           =  (take n xs, drop n xs)
    
    takeWhile, dropWhile    :: (a -> Bool) -> [a] -> [a]
    takeWhile p []          =  []
    takeWhile p (x:xs)
                | p x       =  x : takeWhile p xs
                | otherwise =  []
    
    dropWhile p []          =  []
    dropWhile p xs@(x:xs')
                | p x       =  dropWhile p xs'
                | otherwise =  xs
    
    lines, words     :: String -> [String]
    -- lines "apa\nbepa\ncepa\n" == ["apa","bepa","cepa"]
    -- words "apa  bepa\n cepa"  == ["apa","bepa","cepa"]
    
    unlines, unwords :: [String] -> String
    -- unlines ["apa","bepa","cepa"] == "apa\nbepa\ncepa"
    -- unwords ["apa","bepa","cepa"] == "apa bepa cepa"
    
    reverse          :: [a] -> [a]
    reverse          =  foldl (flip (:)) []
    
    and, or          :: [Bool] -> Bool
    and              =  foldr (&&) True
    or               =  foldr (||) False
    
    any, all         :: (a -> Bool) -> [a] -> Bool
    any p            =  or . map p
    all p            =  and . map p
    
    elem, notElem    :: (Eq a) => a -> [a] -> Bool
    elem x           =  any (== x)
    notElem x        =  all (/= x)
    
    lookup           :: (Eq a) => a -> [(a,b)] -> Maybe b
    lookup key []    =  Nothing
    lookup key ((x,y):xys)
        | key == x   =  Just y
        | otherwise  =  lookup key xys
    
    sum, product     :: (Num a) => [a] -> a
    sum              =  foldl (+) 0
    product          =  foldl (*) 1
    
    maximum, minimum :: (Ord a) => [a] -> a
    maximum []       =  error "Prelude.maximum: empty list"
    maximum xs       =  foldl1 max xs
    
    minimum []       =  error "Prelude.minimum: empty list"
    minimum xs       =  foldl1 min xs
    
    zip              :: [a] -> [b] -> [(a,b)]
    zip              =  zipWith (,)
    
    zipWith          :: (a->b->c) -> [a]->[b]->[c]
    zipWith z (a:as) (b:bs)
                     =  z a b : zipWith z as bs
    zipWith _ _ _    =  []
    
    unzip            :: [(a,b)] -> ([a],[b])
    unzip            =  foldr (\(a,b) ~(as,bs) -> (a:as,b:bs)) ([],[])
    
    nub              :: Eq a => [a] -> [a]
    nub []           = []
    nub (x:xs)       = x : nub [ y | y <- xs, x /= y ]
    
    delete           :: Eq a => a -> [a] -> [a]
    delete y []      = []
    delete y (x:xs)  = if x == y then xs else x : delete y xs
    
    (\\)             :: Eq a => [a] -> [a] -> [a]
    (\\)             = foldl (flip delete)
    
    union            :: Eq a => [a] -> [a] -> [a]
    union xs ys      = xs ++ (ys \\ xs)
    
    intersect        :: Eq a => [a] -> [a] -> [a]
    intersect xs ys  = [ x | x <- xs, x `elem` ys ]
    
    intersperse      :: a -> [a] -> [a]
    -- intersperse 0 [1,2,3,4] == [1,0,2,0,3,0,4]
    
    transpose        :: [[a]] -> [[a]]
    -- transpose [[1,2,3],[4,5,6]] == [[1,4],[2,5],[3,6]]
    
    partition        :: (a -> Bool) -> [a] -> ([a],[a])
    partition p xs   = (filter p xs, filter (not . p) xs)
    
    group            :: Eq a => [a] -> [[a]]
    -- group "aapaabbbeee" == ["aa","p","aa","bbb","eee"]
    
    isPrefixOf, isSuffixOf   :: Eq a => [a] -> [a] -> Bool
    isPrefixOf []     _      =  True
    isPrefixOf _      []     =  False
    isPrefixOf (x:xs) (y:ys) =  x == y && isPrefixOf xs ys
    
    isSuffixOf x y           =  reverse x `isPrefixOf` reverse y
    
    sort              :: (Ord a) => [a] -> [a]
    sort              = foldr insert []
    
    insert            :: (Ord a) => a -> [a] -> [a]
    insert x []       = [x]
    insert x (y:xs)   = if x <= y then x:y:xs else y:insert x xs
    
    --------------------------------------------------------------------------
    -- functions on Char
    
    type String = [Char]
    
    toUpper, toLower :: Char -> Char
    -- toUpper 'a' == 'A'
    -- toLower 'Z' == 'z'
    
    digitToInt :: Char -> Int
    -- digitToInt '8' == 8
    
    intToDigit :: Int -> Char
    -- intToDigit 3 == '3'
    
    ord :: Char -> Int
    chr :: Int  -> Char
    
    isDigit :: Char -> Bool
    
    --------------------------------------------------------------------------
    -- input/output
    
    putStr  :: String -> IO ()
    getLine :: IO String
    
    readFile  :: FilePath -> IO String
    writeFile :: FilePath -> String -> IO ()
    
    --------------------------------------------------------------------------