Лучшее чтение потока данных в Haskell - PullRequest
2 голосов
/ 19 мая 2010

Я пытаюсь проанализировать входной поток, где первая строка сообщает мне, сколько строк данных. Я заканчиваю со следующим кодом, и он работает, но я думаю, что есть лучший способ. Есть?

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)

Примечание: код решает проблему сферы https://www.spoj.pl/problems/PALIN/, но я не думал, что публикация остальной части кода повлияет на обсуждение того, что делать здесь.

Ответы [ 4 ]

7 голосов
/ 19 мая 2010

Используйте replicate и sequence_.

main, proc :: IO ()

main = do numCases <- getLine
          sequence_ $ replicate (read numCases) proc

proc = do str <- getLine
          putStrLn $ findNextPalin str

sequence_ принимает список действий и последовательно запускает их одно за другим. (Затем он отбрасывает результаты; если бы вас интересовали возвращаемые значения из действий, вы бы использовали sequence.)

replicate n x составляет список длиной n, где каждый элемент равен x. Поэтому мы используем его для составления списка действий, которые мы хотим выполнить.

3 голосов
/ 19 мая 2010

Дейв Хинтон ответ верен, но в качестве отступления вот еще один способ написания того же кода:

import Control.Applicative

main = (sequence_ . proc) =<< (read <$> getLine)

proc x = replicate x (putStrLn =<< (findNextPalin <$> getLine))

Просто чтобы напомнить всем, что блоки do не нужны! Обратите внимание, что в приведенном выше тексте =<< и <$> означают простое старое применение функции . Если вы игнорируете оба оператора, код читается точно так же, как это делают чистые функции с аналогичной структурой. Я добавил несколько бесплатных скобок, чтобы сделать вещи более явными.

Их целью является то, что <$> применяет обычную функцию внутри монады, тогда как =<< делает то же самое, но затем сжимает дополнительный слой монады (например, превращая IO (IO a) в IO a).

Интересная часть взгляда на код таким образом заключается в том, что вы в основном можете игнорировать, где находятся монады и тому подобное; как правило, существует очень мало способов разместить операторы «приложения функции», чтобы заставить типы работать.

0 голосов
/ 20 мая 2010

При решении проблем SPOJ в Haskell старайтесь вообще не использовать стандартные строки. ByteStrings намного быстрее, и я обнаружил, что вы обычно можете игнорировать количество тестов и просто запустить карту для всего , но первой строки, например:

{-# 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

Страница SPOJ в Haskell Wiki дает много хороших советов о том, как читать Ints из ByteStrings, а также о том, как обрабатывать большое количество входных данных. Это поможет вам избежать превышения срока.

0 голосов
/ 20 мая 2010

Вы (и предыдущие ответы) должны усердно работать, чтобы отделить IO от логики. Сделайте основной сбор ввода и отдельно (чисто, если возможно) сделайте работу.

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
...