The underscore character is a wildcard that matches anything.
The aim is to create the fizzbuzz functions, which takes a number and returns "Fizz" if the number is a multiple of 3, "Buzz" if the number is a multiple of 5, "Fizzbuzz" if the number is a multiple of 15, and otherwise just the number.
We can evaluate the mod of the number against both 3 and 5 at the top of the function and match the results in a tuple. We don't have to check explicitly for a multiple of 15 because multiples of 15 are the numbers that are multiples of both 3 and 5.
fizzbuzz :: Int -> String
fizzbuzz x =
case (x `mod` 3, x `mod` 5) of
(0, 0) -> "Fizzbuzz"
(0, _) -> "Fizz"
(_, 0) -> "Buzz"
(_, _) -> show x
You know that nagging feeling that there is a better way. Caused by the fact that the text strings Fizz and Buzz show up in two places.
We can't return the number as the final clause of the function because the return value has to be a string - we use the function show to convert a string.
The command :load will load your code into the REPL.
Therefore the function fizzbuzz has type Int goes to string:
*Main> :t fizzbuzz
fizzbuzz :: Int -> String
And I can view thr fizzbuzzes from 1 to 100 in the REPL:
*Main> map fizzbuzz [1..100]
["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11",
"Fizz","13","14", "Fizzbuzz","16","17","Fizz","19","Buzz",
"Fizz","22","23","Fizz","Buzz","26", "Fizz","28","29",
"Fizzbuzz","31","32","Fizz","34","Buzz","Fizz","37","38",
"Fizz","Buzz","41","Fizz","43","44","Fizzbuzz","46",
"47","Fizz","49","Buzz", "Fizz","52","53","Fizz","Buzz",
"56","Fizz","58","59","Fizzbuzz","61","62",
"Fizz","64","Buzz","Fizz","67","68","Fizz","Buzz",
"71","Fizz","73","74" ,"Fizzbuzz","76","77","Fizz",
"79","Buzz","Fizz","82","83","Fizz","Buzz"
,"86","Fizz","88","89","Fizzbuzz","91",
"92","Fizz","94","Buzz","Fizz",
"97","98","Fizz","Buzz"]
*Main>
OK, but instead of seeing these in the REPL I want to print them out. Instead of mapping to a string I want to map fizzbuzz to an IO action that writes a string to the terminal, plus a line feed. The library function I want is putStrLn:
*Main> :t putStrLn
putStrLn :: String -> IO ()
So the function to call fizzbuzz and get the IO action to write the reulting string to the screen is the combination of these, created by function composition operator which is a dot . thus
*Main> :t (putStrLn . fizzbuzz)
(putStrLn . fizzbuzz) :: Int -> IO ()
Right, so I want to map this combined function onto the integer list 1..100. We need the right version of the map function, though, this one:
*Main> :t mapM_
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
So mapM_ will take our function that goes from an Integer to an IO action, apply this to a list of Integers, and give us back an IO action that we can plug straight into the main function, like this:
main :: IO ()
main = do
mapM_ (putStrLn . fizzbuzz) [1..100]
fizzbuzz :: Int -> String
fizzbuzz x =
case (x `mod` 3, x `mod` 5) of
(0, 0) -> "Fizzbuzz"
(0, _) -> "Fizz"
(_, 0) -> "Buzz"
(_, _) -> show x
So, in DOS:
>ghc fizzbuzz
[1 of 1] Compiling Main ( fizzbuzz.hs, fizzbuzz.o )
Linking fizzbuzz.exe ...
>fizzbuzz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
...
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
Well, I got that right but only because I've been round this loop a few times before.
No comments:
Post a Comment