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.
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 )
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.)
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
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
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.
Try :type
on the following functions: even
, id
, pred
, abs
, signum
, log
, and logBase
. Then try some calls to the functions.
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.
What is Word
?
Try minBound
and maxBound
with various types.
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.
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.
Create a file named simple.hs
and work through the examples on slide 75-77 with running ghci
, using :load
, :reload
, etc.
Write some single-argument functions:
isZero x
-- Returns true iff x is zero. Write two versions, one testing equal to 0 and another testing 0.0, and note the difference in types.
isNonZero x
-- Returns true iff x is non-zero.
ltRecip x
-- True iff x is less than its reciprocal (use recip
).
ltSelf x
-- True iff x is less than itself
isIOU c
-- True iff c is 'I' or 'O' or 'U' (caps only)
notIsIOU c
-- opposite of IsIOU
area r
-- computes area of circle with radius r
Define the above using only operators, which are on slide 96. Don't use if-else
s 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
Get creative: Think up three more significantly different single-argument functions. Mail us if you come up with some good ones.
Make a copy of simple.hs
from above and experiment with indentation and continuation as shown on slides 107-108
Can the lines of typespecs.hs
on 103 be in any order?
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.)
The material on partial applications is critical! Here's a whole batch of exercises on partial applications.
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!
Given let plus=(+)
, what's plus
?
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
Get creative: Imagine a function type and then write a function that will be inferred to have that type. Do two more.
::type
construct. Look at where it appears in H10.
:?
being "the conditional operator". Mail us with your findings.
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
Given tempTo
in the previous problem, create toC
and toF
:
> toC 212 100.0 > toF 0 32.0
toC = tempTo 'c' toF = tempTo 'f'
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?
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)
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
[1,[2,3],4]
a valid list? All the values are integers, right?
headsSame list1 list2
that returns true iff the heads of the two lists are the same.
Predict the type of head
, tail
, take
, drop
, reverse
, init
, and last
. Use :t
to check yourself.
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.
Experiment and see whether the arithmetic sequence notation works with arbitrary expressions or only literals.
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]
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.)
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.
[10..1]
produces an empty list. What's an easy way to create a list running from 10 down to 1?
reverse [1..10]
What is the value and type of each of the following expressions?
1:2.0:[] []:[] [1]:[] [1]:[]:[] []:[1]:[] [length] [tail,init] [head,tail]
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):[]]