0

我是一个haskell初学者。我的代码:

module Main (main) where

import System.IO

--Type for identifying Students.
data Student = Student {
        name :: String,
        regnum :: Integer
    }   deriving (Show, Read)

getData :: IO (String)
getData = do
    putStr "\tEnter the student's name: "
    name <- getLine
    putStr "\tEnter the registration number: "
    regstr <- getLine
    let regno = (read regstr) :: Integer
    return (show ( Student name regno ))

addData :: String -> IO (Bool)
addData filename = do
    fileData <- getData
    appendFile filename fileData
    return True

printData :: String -> IO (Bool)
printData filename = do
    fileData <- readFile filename
    putStrLn fileData
    return True

resetFile :: String -> IO (Bool)
resetFile filename = writeFile filename "" >> return True

action :: Char -> String -> IO (Bool)
action option filename =
    if option == '1'
        then addData filename
    else if option == '2'
        then printData filename
    else if option == '3'
        then resetFile filename
    else return False

main = do 
        putStr "What do you want to do?\n\t1) Add a new record to a file.\n\t2) Read a record from a file.\n\t3) Reset the file.\n>> "
        option <- getChar
        action option "records.txt"

我得到的输出是:

What do you want to do?
    1) Add a new record to a file.
    2) Read a record from a file.
    3) Reset the file.
>> 1
    Enter the student's name:   Enter the registration number: 

因此,我无法为学生的姓名提供输入。我在ghci上运行代码。当我试图查看它如何作为可执行文件运行时,它给出了更奇怪的输出。

What do you want to do?
    1) Add a new record to a file.
    2) Read a record from a file.
    3) Reset the file.

(注意它不打印“>>”)。只有在我按两次 Enter 后,它才会打印“>>”。我不明白这里发生了什么。非常欢迎对我的代码进行其他改进。


编辑:: 通过使用 getLine 而不是 getChar ,该程序可以在 ghci 上运行(感谢 Daniel Fischer)。但是编译的时候还是不行。新的输出是:

What do you want to do?
    1) Add a new record to a file.
    2) Read a record from a file.
    3) Reset the file.
1   (Typed my me)
Tom (Typed my me)
234 (Typed my me)
>> Enter the student's name: Enter the registration number:

在重新运行以读取文件时:

What do you want to do?
    1) Add a new record to a file.
    2) Read a record from a file.
    3) Reset the file.
2   (Typed my me)
>> Student {name = "Tom", regnum = 234}

为什么putStr输入后会打印“>>”和getData?

4

1 回答 1

4
option <- getChar

使用默认缓冲设置,程序仅在输入换行符后接收输入。但是getChar, 仅删除从输入缓冲区输入的第一个字符,因此以下getLine内容会从输入缓冲区中读取,直到找到换行符为止。在你的情况下,立即开始。

您可以(至少在 *nix-ish 系统上,缓冲控制过去在 Windows 上无法正常工作,我不知道现在是否可以)通过关闭缓冲来解决问题stdin

main = do
    hSetBuffering stdin NoBuffering
    ...

所以在getChar没有输入换行符的情况下接收输入。或者,不要使用getCharfor 选项,而是使用

(option:_) <- getLine

所以输入缓冲区中没有任何东西可以自动满足以下getLine.

此外,要在输入之前打印出提示,请调用

hFlush stdout

刷新输出缓冲区,或关闭缓冲stdout

最简单的可能是定义

prompt :: String -> IO ()
prompt msg = do
    putStr msg
    hFlush stdout

并将调用替换为putStr调用prompt


对编码风格的批评:

getData :: IO (String)

您将IO类型构造函数的参数括在括号中。这不是必要的和单调的。正常的写法是IO String。(您可能在某处看到了括号IO (),但这些括号不是不包含任何类型的括号,这是该类型的类型构造函数()。令人困惑?是的。)

action :: Char -> String -> IO (Bool)
action option filename =
    if option == '1'
        then addData filename
    else if option == '2'
        then printData filename
    else if option == '3'
        then resetFile filename
    else return False

那应该变成一个case

action option filename
    = case option of
        '1' -> addData filename
        '2' -> printData filename
        '3' -> resetFile filename
        _   -> return False

除此之外,代码看起来很干净。

于 2012-07-14T13:19:48.680 回答