从哈斯克尔的斯坦丁读书的正确方法

时间:2015-03-15 13:06:44

标签: haskell

我有一个程序,根据给出的参数以不同的方式工作:

  • 如果有2个参数 - 它将第二个参数作为文件名,从中读取然后将其打印出来。
  • 如果有1个参数 - 它从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!"

问题是,当它从stdin读取时,在每个新行(输入按下)之后,它打印该行而不是下一个读取。我需要它等待整个输入而不是打印出来(在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

谢谢。

2 个答案:

答案 0 :(得分:4)

这里的问题是getContents使用惰性IO,使得输入流逐行处理。如果要在开始执行作业之前强制它读取整个输入,可以使用以下hack:

...
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的严格IO模块。例如,我刚发现提供System.IO.Strict.getContentsstrict-io包。使用它你应该能够写(未经测试)

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

答案 1 :(得分: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