2015-03-15 4 views
1

У меня есть программа, которая в зависимости от заданных параметров работы по-разному:Правильный способ чтения из стандартного ввода в Haskell

  • Если есть 2 аргументы - это занимает 2 аргумент как имя файла, считывает из него и затем просто распечатывает его.
  • Если есть один аргумент - он читается из stdin, а также распечатывает его.

Вот код:

main :: IO() 
main = do 

-- Read given arguments 
args <- getArgs 

-- If file containing gramma was given 
if length args == 2 then do 
    hfile <- openFile (last args) ReadMode 
    content <- hGetContents hfile 
    let inGramma = getGramma content 
    doJob (head args) inGramma 
    hClose hfile 
    return() 

-- If no file was given - reads from stdin 
else if length args == 1 then do 
    content <- getContents 
    let inGramma = getGramma content 
    doJob (head args) inGramma 
    return() 

else do putStrLn "Invalid count of arguments!" 

Проблема заключается в том, когда он читает из стандартного ввода, после каждой новой строки (введите прессованные), она выводит эту строку и чем читает дальше. Мне нужно, чтобы он подождал весь ввод и распечатал его (после Ctrl + D).

Вот функции, используемые в этом коде:

-- | Structure of gramma 
data GrammaStruct = Gramma 
    { nonTerminals :: NonTerminals 
    , terminals :: Terminals 
    , start :: Start 
    , rules :: Rules 
    } deriving (Eq) 

-- | Representation of gramma 
instance Show GrammaStruct where 
    show (Gramma n t s r) = 
       init (showSplit n) ++ 
     "\n" ++ init (showSplit t) ++ 
     "\n" ++ showStart s ++ 
     "\n" ++ init (showRules r) 

-- | Print gramma 
showGramma :: GrammaStruct -> IO() 
showGramma gr = do 
    putStrLn $ show gr 

-- | Transforms string given from file of stdin into gramma representation in app 
getGramma :: String -> GrammaStruct 
getGramma hIn = procLns (lines hIn) 

-- | Depending on option given, provides required functionality 
doJob :: String -> GrammaStruct -> IO() 
doJob op gramma 
     | op == "-i" = showGramma gramma 

Спасибо.

+0

Не то, чтобы вопрос был _wrong_, как таковой, но «дождаться всего ввода и затем распечатать его» - это довольно антимодульное требование. Я не удивлюсь, если это требование исчезнет или даже станет проблемой для вас позже в разработке этого инструмента. –

ответ

3

Проблема заключается в том, что getContents использует ленивый ввод-вывод, делая поток ввода обрабатываемым по очереди. Если вы хотите, чтобы заставить его прочитать весь ввод перед началом выполнения задания вы можете использовать следующий хак:

... 
if length args == 1 then do 
    content <- getContents 
    length content `seq` return() -- force the input to be fully read now 
    let inGramma = getGramma content 
    doJob (head args) inGramma 
    return() 

В качестве альтернативы, используйте evaluate, или посмотреть в Hackage для строгого модуля ввода-вывода обеспечивает строгую getContents. Например, я только что нашел пакет strict-io, содержащий System.IO.Strict.getContents. Пользуясь тем, что вы должны быть в состоянии написать (непроверенные)

import qualified System.IO.Strict as S 
... 
if length args == 1 then do 
    content <- run S.getContents 
    ... 
+0

Спасибо за ответ, что взлом с 'seq' работает так, как мне нужно. – boyks

1

Это не отвечает на вопрос, но вместо того, чтобы испытывать длину аргументов, вы можете сопоставить шаблон против него и возвратит содержание

main = do 
    let (opt, contentM) = case getArgs of 
       [opt]  -> (op, getContent) 
       [opt, file] -> (op, hGetContent file) 
       _   -> error ("Invalid count of argument") 
    inGramma <- fmap getGramma contentM 
    doJob opt inGramma 
правильно
Смежные вопросы