Better data reading in Haskell
I am trying to parse an input stream where the first line tells me how many lines of data there are. I get the following code and it works, but I think there is a better way. Here?
main = do
numCases <- getLine
proc $ read numCases
proc :: Integer -> IO ()
proc numCases
| numCases == 0 = return ()
| otherwise = do
str <- getLine
putStrLn $ findNextPalin str
proc (numCases - 1)
Note. The code solves the Sphere issue https://www.spoj.pl/problems/PALIN/ , but I didn't think posting the rest of the code would affect the discussion of what to do here.
a source to share
Use replicate
and sequence_
.
main, proc :: IO ()
main = do numCases <- getLine
sequence_ $ replicate (read numCases) proc
proc = do str <- getLine
putStrLn $ findNextPalin str
sequence_
takes a list of actions and runs them one by one in sequence. (It then discards the results, if you are interested in return values from actions you should use sequence
.)
replicate n x
composes a list of length n
, each element of which x
. Therefore, we use it to create a list of actions that we want to run.
a source to share
Dave Hinton's answer is correct, but alternatively another way of writing the same code:
import Control.Applicative
main = (sequence_ . proc) =<< (read <$> getLine)
proc x = replicate x (putStrLn =<< (findNextPalin <$> getLine))
Just to remind everyone that blocks are do
not needed! Please note that in the above example =<<
and <$>
are included in the regular old application... If you ignore both statements, the code looks exactly the same as similar structured pure functions. I've added some free parentheses to make things more explicit.
Their purpose is to <$>
apply a regular function inside the monad, and =<<
do the same, but then shrink the extra layer of the monad (for example, turning IO (IO a)
into IO a
).
The interesting part of looking at the code this way is that you can basically ignore where the monads and those are; there are generally very few ways to accommodate the "functional application" statements for these types to work.
a source to share
You (and the previous answers) have to work hard to decouple the IO from the logic. Do a basic collection of inputs and separately (cleanly if possible) do the work.
import Control.Monad -- not needed, but cleans some things up
main = do
numCases <- liftM read getLine
lines <- replicateM numCases getLine
let results = map findNextPalin lines
mapM_ putStrLn results
a source to share
When solving SPOJ problems in Haskell, try not to use standard strings at all. ByteStrings are much faster and I found that you can usually ignore the number of tests and just run the map on top of everything except the first line, like this:
{-# OPTIONS_GHC -O2 -optc-O2 #-}
import qualified Data.ByteString.Lazy.Char8 as BS
main :: IO ()
main = do
(l:ls) <- BS.lines `fmap` BS.getContents
mapM_ findNextPalin ls
The SPOJ page on the Haskell Wiki gives many good pointers on how to read Ints from ByteStrings, as well as how to deal with a lot of inputs. This will help you avoid exceeding the deadline.
a source to share