User:Davjam2/Solutions/Numbers
Numeric Types[edit | edit source]
1.
These are initial thoughts on appropriate types. The precise requirements may indicate different types are needed.
- Physical measurements of room and furniture sizes are approximations and probably only accurate to 3 or 4 significant figures.
Float
would be a good choice for an internal representation, storing the dimensions in some particular unit (e.g. metres). You could then round to centimetres, or convert to/from feet and inches, etc, for interaction with the user. - Prime numbers are positive integers, and large prime numbers will be outside the limit of numbers that can be represented by an
Int
.Natural
orInteger
would be good choices. Centi
would be a good choice for most currencies (where the main monetary unit is divided into 100 subunits). If the program needs to deal accurately with other currencies (the Maltese Scudo is an extreme, and unlikely, possibility), other types may be required.Int8
orWord8
would be good choices. The fact that they perform modulo arithmetic (and don't give overflow errors) is advantageous here.- Either
Float
orDouble
, depending on the accuracy required. The hypotenuse of a triangle where the other two sides are both length 1 is , which can't be expressed exactly in any of the standard types. Returning aRational
would (incorrectly) suggest an accurate rational result. Given the result of the function will be aFloat
orDouble
approximation, the inputs might as well be approximations (to the same precision) too.
2.
squareInteger :: Integer -> Integer
squareInteger x = x * x
squareRational :: Rational -> Rational
squareRational x = x * x
3.
triangleArea :: Double -> Double -> Double -> Double
triangleArea a b x = 0.5 * a * b * sin x
4.
halvings :: [Rational]
halvings = iterate (* half) half
where half = 1 % 2
5.
Probably not. It can't represent any more than Rational
can (which already offers unlimited size and precision). And it can't represent e.g. exactly, unlike Rational
.
A better idea might be to write (or find) functions to format Rational
numbers as exact decimals when possible or highlight recurring digits when not. (You might like to try this!)
What Numeric Means (The Num Class)[edit | edit source]
1.
You can check against the actual definition, for example:
> :info Float ... instance Ord Float -- Defined in ‘GHC.Classes’ instance Enum Float -- Defined in ‘GHC.Float’ ...
Float
being an Enum might be a bit surprising!
2.
div
will work with types that only represent integers and will return an integer result. There is also a function mod
that will return the remainder. (There's also a divMod
that does both together).
(/)
will work with types also represent non-integer values, and will return the exact (for e.g. Rational
) or a precise-but-not-exact (for e.g. Float
) result of the division, with no concept of "remainder".
3.
square :: Num a => a -> a
square x = x * x
Note you can now do any of the following:
> square 3 :: Int 9 > square 3 :: Float 9.0 > square 3 :: Rational 9 % 1
Accuracy and Precision[edit | edit source]
1.
binaryParts :: Rational -> [Rational]
binaryParts = go halvings
where
go (h:hx) x = case h `compare` x of
EQ -> [h]
LT -> h : go hx (x - h)
GT -> go hx x
Interestingly, the go
function takes an infinite list as a parameter and can return an infinite list as a result. Due to lazy evaluation, this all works fine.
Overflow[edit | edit source]
1.
w8Add :: Word8 -> Word8 -> (Bool, Word8)
w8Add x y = (z < x, z) where z = x + y
y is a positive integer or zero (Word8
is unsigned). Hence adding y to x should give a value greater than or equal to x. If it's less, overflow must have occurred. Is it possibl for overflow to occur yet the result be bigger than x? No: the biggest y can be is 255, and x + 255 is always one less than x (except when x is 0, when no overflow happens).
2.
w8AddErr :: Word8 -> Word8 -> Word8
w8AddErr x y | z < x = error "overflow"
| otherwise = z
where z = x + y
3.
Here are two different solutions for i8Add
, each illustrating some (mildly interesting) Haskell. The first uses a single where
for two different guards:
i8Add :: Int8 -> Int8 -> (Bool, Int8)
i8Add x y | y > 0 = (z < x, z)
| otherwise = (z > x, z)
where z = x + y
The second shows a local function binding:
i8Add :: Int8 -> Int8 -> (Bool, Int8)
i8Add x y = (z `cmp` x, z)
where
z = x + y
cmp = if y > 0 then (<) else (>)
i8AddErr
seems a bit easier with the second version:
i8AddErr :: Int8 -> Int8 -> Int8
i8AddErr x y | z `cmp` x = error "overflow"
| otherwise = z
where
z = x + y
cmp = if y > 0 then (<) else (>)
Division by Zero[edit | edit source]
1.
isValidFloat :: Float -> Bool
isValidFloat x | isInfinite x = False
| isNaN x = False
| otherwise = True
or:
isValidFloat x = not (isInfinite x) && not (isNaN x)
2.
import Control.Exception --this needs to be at the top of the file
assertValidFloat :: Float -> a -> a
assertValidFloat x = assert (isValidFloat x)
Note that this is equivalent to:
assertValidFloat x y = assert (isValidFloat x) y
If you're wondering why the y is not needed, read this.
Enumerations[edit | edit source]
1.
The enumFrom
functions represent the different combinations of [x..]
, [x,y,..]
etc. The start value must always be present. The next value and final value are optional, so there are four variations: enumFrom
, enumFromThen
, enumFromTo
and enumFromThenTo
.
2.
Try for yourself! BUT BE CAREFUL with [120 ..] :: [Integer]
. You might want to type take 30 $ [120 ..] :: [Integer]
Note the rounding errors with Float
and how the results are consistent with the behaviour of succ
.
3.
enumerate :: (Enum a, Bounded a) => [a]
enumerate = [minBound .. maxBound]
You can use it like this:
> enumerate :: [Bool] [False,True] > enumerate :: [Ordering] [LT,EQ,GT]
4.
It won't for the minBound, for example:
> succ (pred 0) :: Word8 *** Exception: Enum.pred{Word8}: tried to take `pred' of minBound
5.
You would need to specify the type:
> toEnum (fromEnum EQ) :: Ordering EQ > toEnum (fromEnum EQ) :: Bool True
For enumerations with more values than can be represented by an Int
, "wrapping" may occur:
> toEnum (fromEnum 2158269056624017538838) :: Integer -234
but:
> succ (pred 2158269056624017538838) 2158269056624017538838
is fine.
Type Classes[edit | edit source]
1.
Check for yourself!
2.
triangleArea :: Floating a => a -> a -> a -> a
triangleArea a b x = 0.5 * a * b * sin x
Note how this now has exactly the same class constraint as the sin
function that we use, which makes complete sense.
3.
isValidFloat :: RealFloat a => a -> Bool
isValidFloat x | isInfinite x = False
| isNaN x = False
| otherwise = True
assertValidFloat :: RealFloat a => a -> b -> b
assertValidFloat x = assert (isValidFloat x)
4.
Probably: Num
, Real
, Integral
, as well as Eq
, Ord
, Enum
and Bounded
.
5.
Probably: Num
, Real
, Fractional
. Ideally also Eq
, Ord
(though we may struggle to implement these).
(Real
has a method toRational
defined as "the rational equivalent of its real argument with full precision" which would be impossible to implement as stated. It would probably be appropriate to return a rational approximation to some stated precision.)
It can't be Floating
, since (for example) we can't represent .