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.

+2


a source to share


4 answers


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.

+7


a source


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.

+3


a source


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

      

0


a source


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.

0


a source







All Articles