module Cases where
import Question
booleanAlgebraUrl = "http://en.wikipedia.org/wiki/Boolean_algebra"
cases = do
h2 <#>Definition by cases#>
<#>Implement a function which returns 1 if its argument is between 0
and 1, inclusive, and 0 otherwise.#> ?> do
ul $ do
li $ p <#>Name: pulse, if we view this function as
the representation of a signal which is active from 0
to 1.#>
li $ p <#>Arguments: x.#>
li $ do
p <#>Comment:#>
fixed $ unlines
[ "-- pulse returns 1 for arguments between 0 and 1, inclusive, and 0"
, "-- otherwise."
]
li $ p <#>Type signature: pulse :: Double -> Double. We
could also be more general. The only requirement on
the output is that it should be a number, and the
input should also be ordered: (Ord a, Num a, Num
b) => a -> b. We don't expect you to write
type signatures like this one, though.#>
li $ do
p <#>Properties:#>
ul $ do
li $ fixed $ unlines
[ "prop_pulse_1 :: Double -> Property"
, "prop_pulse_1 x = 0 <= x && x <= 1 ==> pulse x == 1"
]
li $ fixed $ unlines
[ "prop_pulse_0 :: Double -> Property"
, "prop_pulse_0 x = x < 0 || x > 1 ==> pulse x == 0"
]
p <#>We use (==>) to restrict the test to inputs
satisfying the precondition to the left of
(==>). Inputs that do not satisfy the
precondition are discarded. A test counts as passed
or not passed only when the inputs do satisfy the
precondition.#>
p <#>Note that we have to change the type of the property
when using (==>).#>
li $ do
p <#>Two ways of solving the problem:#>
ul $ do
li $ fixed $ unlines
[ "-- pulse returns 1 for arguments between 0 and 1, inclusive, and 0"
, "-- otherwise."
, "pulse :: Double -> Double"
, "pulse x | 0 <= x && x <= 1 = 1"
, " | x < 0 = 0"
, " | x > 1 = 0"
]
li $ do
fixed $ unlines
[ "-- pulse returns 1 for arguments between 0 and 1, inclusive, and 0"
, "-- otherwise."
, "pulse :: (Ord a, Num a, Num b) => a -> b"
, "pulse x | 0 <= x && x <= 1 = 1"
, " | otherwise = 0"
]
p <#>Remember that guards are checked beginning with the
top-most one. The first one which evaluates to
True is the one selected.#>
p <#>Note that the properties above are almost like a
reimplementation of pulse. It is easy to understand
pulse by looking at its definition, and the
properties, which are arguably harder to understand, don't
give us any more information. Hence some may say that there
isn't much point in writing properties like the ones
above. You can decide for yourselves.#>
<#>Write a function which checks whether a point lies in the
closed unit square centred around the origin.#> ?> do
p <#>You can represent a point in a variety of different ways. The
following solutions assume that you have not heard about
data types or tuples yet. If you have heard about data types,
then you should use something like the following to represent
a point:#>
fixed "data Point = Point { x, y :: Double }"
p <#>A couple of possible solutions, the last of which is the
preferred one:#>
ul $ do
li $ fixed $ unlines
[ "-- Given two arguments, an x coordinate and a y coordinate, returns"
, "-- True if the corresponding point lies in or on the unit square"
, "-- centred around the origin."
, "unitSquare :: Double -> Double -> Bool"
, "unitSquare x y | abs x <= 0.5 && abs y <= 0.5 = True"
, " | otherwise = False"
]
li $ fixed $ unlines
[ "-- Given two arguments, an x coordinate and a y coordinate, returns"
, "-- True if the corresponding point lies in or on the unit square"
, "-- centred around the origin."
, "unitSquare :: Double -> Double -> Bool"
, "unitSquare x y = if abs x <= 0.5 && abs y <= 0.5 then"
, " True"
, " else"
, " False"
]
li $ fixed $ unlines
[ "-- Given two arguments, an x coordinate and a y coordinate, returns"
, "-- True if the corresponding point lies in or on the unit square"
, "-- centred around the origin."
, "unitSquare :: Double -> Double -> Bool"
, "unitSquare x y = abs x <= 0.5 && abs y <= 0.5"
]
p <#>Sometimes it is hard to come up with good properties for a
function. In that case one can write down something like the
following:#>
fixed $ unlines
[ "prop_unitSquare :: Bool"
, "prop_unitSquare ="
, " unitSquare 0.5 (-0.5) == True &&"
, " unitSquare 0.5 0.5 == True &&"
, " unitSquare 1.0 0.0 == False &&"
, " unitSquare 0.0 0.0 == True"
]
p <#>This doesn't provide very good test coverage, and isn't good
documentation either, but at least it can catch some mistakes.
However, for this particular example we can come up with
rather interesting properties, for instance the following ones
based on the symmetry of the square around the coordinate
axes.#>
ul $ do
li $ fixed $ unlines
[ "prop_unitSquareX :: Double -> Double -> Bool"
, "prop_unitSquareX x y = unitSquare x y == unitSquare (-x) y"
]
li $ fixed $ unlines
[ "prop_unitSquareY :: Double -> Double -> Bool"
, "prop_unitSquareY x y = unitSquare x y == unitSquare x (-y)"
]
<#>Define a function which returns True iff both its
arguments are True.#> ?> do
ul $ do
li $ p <#>Name: and is already defined by the
Prelude, so we choose iff (if and only
if). Another suitable choice would be (<% text "<=>"
%>).#>
li $ p <#>Arguments: x, y.#>
li $ p <#>Comment: -- Returns True iff both arguments are
True.#>
li $ p <#>Type signature: iff :: Bool -> Bool -> Bool#>
li $ p <#>Properties: All the axioms of a >boolean algebra should be
satisfied by iff (or (<% text "&&"
%>)) and the or operator, (||).#>
-- li $ do
-- p <#>Properties:#>
-- ul $ do
-- li $ fixed $ unlines
-- [ "prop_iffLeft :: Bool -> Bool -> Property"
-- , "prop_iffLeft x y = x `iff` y ==> x == True"
-- ]
-- li $ fixed $ unlines
-- [ "prop_iffRight :: Bool -> Bool -> Property"
-- , "prop_iffRight x y = x `iff` y ==> y == True"
-- ]
li $ do
p <#>We can use pattern matching to solve this
exercise:#>
fixed $ unlines
[ "-- Returns True iff both arguments are True."
, "iff :: Bool -> Bool -> Bool"
, "iff True True = True"
, "iff _ _ = False"
]
p <#>The character _ is a wildcard. It matches
any input. We could also have written the following
expression, but wildcards are often preferred when the
corresponding value isn't used on the right hand
side.#>
fixed $ unlines
[ "-- Returns True iff both arguments are True."
, "iff :: Bool -> Bool -> Bool"
, "iff True True = True"
, "iff x y = False"
]