Problem
Beginner functional programmer… (but not beginner programmer)
Currently, I have the following code:
import Control.Monad (mapM_)
main = gridMaker 3
gridMaker :: Int -> IO ()
{-Creates a nxn matrix filling the lower triangle with "O"'s
and filling the rest with "X"s
Example:
O --- X --- X
O --- O --- X
O --- O --- O
-}
gridMaker gridSize =
-- Creates the indicators
let startingIter = 1
indicators = indicatorListCreator gridSize startingIter
-- Print each indicator out to IO
in mapM_ linePrinter indicators
indicatorListCreator :: Int -> Int -> [[String]]
{- Build the indicators of
[["O", "X", "X"],["O", "O", "X"] ... and so forth.
Recursively determines how many iterations we've been through,
and determines how many "X"s and "O"s we should have
per each line. -}
indicatorListCreator gridLen iterNum
| iterNum > gridLen = []
| otherwise =
let itersRemaining = gridLen - iterNum
indicator = replicate iterNum "O" ++
replicate itersRemaining "X"
in
indicator: indicatorListCreator gridLen (iterNum + 1)
linePrinter :: [String] -> IO ()
{- Takes the indicators and prints each line accordingly. -}
linePrinter [indicator1, indicator2, indicator3] =
let between = " --- "
outString = indicator1 ++ between ++
indicator2 ++ between ++
indicator3
in putStrLn outString
linePrinter _ = error"Stupidly hardcoded to only show 3x3"
Running and compiling this code results in:
O --- X --- X
O --- O --- X
O --- O --- O
Some of my thoughts…
- Is it possible to build indicatorListCreator out of folds?
- How can I circumvent hardcoding linePrinter
- Is this optimal?
- Where should I improve on coding conventions / style?
- Any other glaring shortcomings?
Thank you in advance!
Solution
I like that you are trying to not use explicit recursions everywhere. That being said, there are a few standard functions that we could use to get a code similar to the following.
import Data.List(intercalate)
grid :: Int -> [String]
grid n = [intercalate " --- " . take n . drop (n-k) $ osxs | k <- [1..n]]
where osxs = replicate n "O" ++ replicate n "X"
printGrid :: Int -> IO ()
printGrid = putStr . unlines . grid
Most notably, Data.List.intercalate
can be used to generalize your linePrinter
, and unlines
helps us avoid mapM_
.
If you are writing a game, then a Data.Map.Strict.Map
could be useful for storing the board’s current values.