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" ]