Haskell Exercises

Note: These exercises roughly follow the topic sequence in the slides. If questions start looking like greek, you've probably hit the end of what we've covered in class.

Some exercises have answers and some don't. If you'd like an answer for any that don't have one, let us (372s18) know. We'll add them as time permits.

  1. Is Haskell used in industry? Try searching some job sites, like Indeed, Monster, and Dice. What industries seem to be using Haskell the most?

  2. Rewrite the following Java expressions using Haskell function call syntax. Use as few parentheses as possible.

    f(x) * a == g(a) + c
        
    h(f(a) + g(x-y))
        
    f(g(a*-b)-h(b))
    
    f(x) * a == g(a) + c
        f x * a == g a + c
    	
    h(f(a) + g(x-y))
        h (f a + g (x - y))
    	
    f(g(a*-b)-h(b))
        f (g (a * (-b)) - h b)
        More idiomatic: f (g (a * negate b) - h b )
    
  3. In ghci use import Data.Char to load the Data.Char module. Then do :browse Data.Char to see all the functions in the module. (You'll see a long data declaration for GeneralCategory , too. Ignore it for now.) Try ord, chr, isPunctuation, isHexDigit, and toTitle. Regarding values for ord and chr, Google for ASCII chart. (See the problem below re pred 'A', too.)

  4. I started memorizing the ASCII codes about 35 years after I should have. I hope you'll start sooner than I did! Here's one way to learn them: http://www.memrise.com/course/80243/ascii-to-decimal
  5. What does succ 'A' produce, and why?

    Also, see if you can find an argument for pred that will produce this exception: Prelude.Enum.Char.pred: bad argument

    Re making pred throw an exception, there's nothing before the first character, which has a numeric value of zero. In Haskell character literals we can use \N to specify the Nth Unicode character. (The first 128 characters of Unicode are the same as ASCII.) Haskell also recognizes ASCII mnemonics like NUL and BEL, too.
    > pred '\0'
    *** Exception: Prelude.Enum.Char.pred: bad argument
    
    > pred '\NUL'
    *** Exception: Prelude.Enum.Char.pred: bad argument
    
  6. Hoogle searches Haskell documentation. For example, hit http://www.haskell.org/hoogle/?hoogle=toUpper to search for functions named toUpper.

    Suggestion: Set up a Chrome "Search Engine" for Hoogle—see O(1) Navigation on the Web with "Custom Search Engines". If you'll set up a search engine for 372 that lets you do things like "372 ." and "372 a2.html" and one for 372pz that goes to Piazza, I'll give you a assignment point of extra credit. Demostrate them to us during office hours.

  7. Try :type on the following functions: even, id, pred, abs, signum, log, and logBase. Then try some calls to the functions.

  8. Try :info on these types/type classes: Eq, Bool, Int, Ord, Bounded. Several end in many lines of this general form: instance (Eq a, Eq b, Eq c) => Eq (a, b, c). Ignore that stuff for now. Focus on what precedes it.

  9. What is Word?

  10. Try minBound and maxBound with various types.

  11. If you've had 352, use grep or two on the output of :browse Prelude to see what the Prelude defines that aren't functions.

  12. The diagram on slide 59 shows a big "X" on the arrow from Eq to Num. That's because I claim that implied relationship doesn't actually exist.

    Use :info on Ord, Num, and Real, and see if you believe my "correction" is correct.

  13. Create a file named simple.hs and work through the examples on slide 75-77 with running ghci, using :load, :reload, etc.

  14. Write some single-argument functions:

    Define the above using only operators, which are on slide 96. Don't use if-elses or guards, which we'll cover later.

    I typically work out small pieces of code at the ghci prompt, and then move that code into a file.

    Use :set +t to cause the type of each to be automatically shown.

    isZero1 x = x == 0
    
    isZero2 x = x == 0.0
    
    isNonZero x = not (isZero1 x) -- note use of not
    
    ltRecip x = x < recip x
    
    ltSelf x = x < x
    
    isIOU c = c == 'I' || c == 'O' || c == 'U'
    
    notIsIOU c = not (isIOU c) -- note use of not
    
    area r = pi * r ^ 2
    
  15. Get creative: Think up three more significantly different single-argument functions. Mail us if you come up with some good ones.

  16. Make a copy of simple.hs from above and experiment with indentation and continuation as shown on slides 107-108

  17. Can the lines of typespecs.hs on 103 be in any order?

  18. If you've got Java 9 installed, try running jshell, a REPL for Java. (It only took 22 years after Java was introduced for a good REPL for Java to appear.)

  19. The material on partial applications is critical! Here's a whole batch of exercises on partial applications.

  20. On slide 92 the names add and plus are both bound to the same function value.

    Speculate: can add and plus be compared?

    Try it!

  21. Given let plus=(+), what's plus?

  22. Define functions f1 through f6 whose types are inferred to be the following. The functions don't need to perform any meaningful computation—only the type matters. You may NOT use ::type (as shown on slide 65) to force types.

    f1 :: Eq a => a -> Bool
    
    f2 :: Num a => t -> a
    
    f3 :: Fractional a => t -> a
    
    f4 :: Int -> Int
    
    f5 :: (Bounded a, Ord a) => a -> Bool
    
    f6 :: (Bounded a, Eq a) => a -> Bool
    
    let f1 x = x == x
    
    let f2 x = 10
    
    let f3 x = 1.0
    
    let f4 x = ord 'a' + x -- assumes ':m +Data.Char' done previously or in ~/.ghci
    
    let f5 x = x > minBound
    
    let f6 x = x == minBound
    
  23. Get creative: Imagine a function type and then write a function that will be inferred to have that type. Do two more.

  24. Remind yourself of the name for the ::type construct. Look at where it appears in H10.
  25. See if I'm right about the official name of Java's :? being "the conditional operator". Mail us with your findings.
  26. Write a function tempTo units temperature to convert a temperature to either Celsius or Fahrenheit. It works like this:

    > tempTo 'c' 32
    0.0
    
    > tempTo 'f' 0
    32.0
    
    tempTo u t
        | u == 'f' = (t * 9/5) + 32
        | u == 'c' = (t - 32) * 5/9
    
  27. Given tempTo in the previous problem, create toC and toF:

    > toC 212
    100.0
    
    > toF 0
    32.0
    
    toC = tempTo 'c'
    toF = tempTo 'f'
    
  28. Slide 4 says, "A paradigm has a world view, a vocabulary, and a set of techniques that can be applied to solve a problem." What are some terms in the vocabulary of functional programming?

  29. Write a function sumDigits that returns the sum of the digits of an integer. Be sure to handle negative values, too.

    > sumDigits (2^200)
    256
    > sumDigits (-123)
    6
    
    sumDigits :: Integral a => a -> a
    sumDigits n
        | n < 0  = sumDigits (negate n)
        | n == 0 = 0
        | otherwise = n `rem` 10 + sumDigits (n `div` 10)
    
  30. Pretend you've never heard of Gauss's summation formulas and write recursive functions sumN and sumMtoN to compute the sum of the first N natural numbers and the natural numbers from M to N, respectively.

    sumN n
        | n == 1 = 1
        | otherwise = n + sumN (n - 1)
        
    sumMtoN from to
        | from > to = 0
        | otherwise = from + sumMtoN (from + 1) to
    
  31. If you're into math, write a recursive function to compute a square root using the Babylonian method. Note: I haven't written such a function myself so there may be trouble I'm not anticipating.

  32. Why isn't [1,[2,3],4] a valid list? All the values are integers, right?

  33. Write a function headsSame list1 list2 that returns true iff the heads of the two lists are the same.

  34. Predict the type of head, tail, take, drop, reverse, init, and last. Use :t to check yourself.

  35. In English, what does t5=take 5 create?

    t5 is a partial application of take that will produce the first five elements of whatever list it's called with.
  36. Experiment and see whether the arithmetic sequence notation works with arbitrary expressions or only literals.

  37. Do let v = [1..7] and using various combinations of head, tail, take, drop, reverse, init, and last write two different expressions that produce each of these values:

    1
    6
    7
    [3,4,5]
    [6,5]
    
  38. Like the previous previous problem but produce "auto", "to", "mat", and "tam" from "automata". (This makes me wonder which English word contains the most words, including reversals.)

  39. Look at the values in these two lists: [1.0,1.1..2] and [1.0,1.2..2]. Do you see an inconsistency? What is it and why does it exist?

    Let's try them:

    > [1.0,1.1..2]
    [1.0,1.1,1.2000000000000002,1.3000000000000003,1.4000000000000004,1.5000000000000004,1.6000000000000005,1.7000000000000006,1.8000000000000007,1.9000000000000008,2.000000000000001]
    
    > [1.0,1.2..2]
    [1.0,1.2,1.4,1.5999999999999999,1.7999999999999998,1.9999999999999998]
    
    The last value in the first list is less than 2.0 but the last value in the second list is greater than 2.0. This is due to issues with the representation of floating-point numbers.
  40. [10..1] produces an empty list. What's an easy way to create a list running from 10 down to 1?

    reverse [1..10]
    
  41. What is the value and type of each of the following expressions?

    1:2.0:[]
    []:[]
    [1]:[]
    [1]:[]:[]
    []:[1]:[]
    [length]
    [tail,init]
    [head,tail]
    
  42. Work through the following operations, drawing the list structure and labeling values.

    let a=5
    let b=[2,3]
    let c=a:b
    let d = a:tail b
    let e = head b:b
    let f = [head b:head c:head (tail d):[]]
    
  43. Experiment with nthOdd = (!!) [1,3..] from the slides. Try :type (!!) and then :type nthOdd.

  44. Try length ([minBound..maxBound]::[Char]). Try it again with s/Char/Bool/ (UNIX-speak for change Char to Bool). Try it without ::[type].