开发者

Better data stream 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'm ending up with the following code, and it works, but I think there is a better way. Is there?

main = do
    numCases <- getLine
    proc $ read numCases

proc :: Integer -> IO ()
proc numCases
     | numCases == 0 = return ()
     | otherwi开发者_如何学运维se = do
         str <- getLine
         putStrLn $ findNextPalin str
         proc (numCases - 1)

Note: The code solves the Sphere problem https://www.spoj.pl/problems/PALIN/ but I didn't think posting the rest of the code would impact the discussion of what to do here.


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 after the other, in sequence. (Then it throws away the results; if you were interested in the return values from the actions, you'd use sequence.)

replicate n x makes a list of length n, with each element being x. So we use it to build up the list of actions we want to run.


Dave Hinton's answer is correct, but as an aside here's 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 do blocks aren't necessary! Note that in the above, both =<< and <$> stand in for plain old function application. If you ignore both operators, the code reads exactly the same as similarly-structured pure functions would. I've added some gratuitous parentheses to make things more explicit.

Their purpose is that <$> applies a regular function inside a monad, while =<< does the same but then compresses an extra layer of the monad (e.g., turning IO (IO a) into IO a).

The interesting part of looking at code this way is that you can mostly ignore where the monads and such are; typically there's very few ways to place the "function application" operators to make the types work.


You (and the previous answers) should work harder to divide up the IO from the logic. Make main gather the input and separately (purely, 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


When solving SPOJ problems in Haskell, try not to use standard strings at all. ByteStrings are much faster, and I've found you can usually ignore the number of tests and just run a map over everything but the first line, like so:

{-# 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 in the Haskell Wiki gives a lot of good pointers about how to read Ints from ByteStrings, as well as how to deal with a large quantities of input. It'll help you avoid exceeding the time limit.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜