2012-07-14 86 views
0

我是一名haskell初學者。我的代碼:無法同時接受2個輸入

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中(感謝丹尼爾·菲捨爾)。但編譯時仍然不起作用。新的輸出是:

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} 

爲什麼是「>>」,並採取了輸入後正在打印的getData的putStr S'

+0

你使用的是Windows嗎? – 2012-07-14 13:19:56

+0

@ n.m。是。我在窗戶上。 – Likhit 2012-07-14 13:20:46

+2

不要使用'getChar'獲取交互式輸入,它不會按預期工作。 'hSetBuffering stdin NoBuffering'可能會或可能不會幫助到這裏,因爲終端可能會做自己的行緩衝。 – 2012-07-14 13:36:51

回答

4
option <- getChar 

使用默認的緩衝設置,程序僅在輸入換行符後接收輸入。但是,getChar僅刪除從輸入緩衝區中輸入的第一個字符,因此以下getLine將從輸入緩衝區讀取,直到找到換行符。在你的情況下,立即在開始。

您可以通過關閉緩存爲stdin解決問題(至少在* nix十歲上下的系統,用於無法在Windows正常運行緩衝控制,如果現在不,我不知道),

main = do 
    hSetBuffering stdin NoBuffering 
    ... 

因此getChar接收輸入時沒有輸入換行符。或者,而不是使用getChar的選項,使用

(option:_) <- getLine 

所以不會留在輸入緩衝器什麼自動滿足以下getLine

此外,爲了獲得在輸入​​輸入之前打印出的提示,呼叫

hFlush stdout 

到刷新輸出緩衝區,或關閉緩衝爲stdout

最簡單的可能是定義

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

,並調用prompt更換調用putStr


的編碼風格批判:

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 

除此之外,代碼看起來很乾淨。

+0

你能否詳細說明'(:_)'在'(option:_)'中做了些什麼?它是一個運營商嗎? – Likhit 2012-07-14 13:25:50

+0

'所以getChar接收輸入時沒有輸入新行 - 不,這不起作用(至少在Linux上)。 – 2012-07-14 13:26:39

+4

這是一種模式匹配。 '(:)'是列表類型的構造函數,'_'是通配符模式。因此'(選項:_)< - getLine'表示「調用getLine,將選項綁定到第一個字符並忽略其餘部分」。 – 2012-07-14 13:27:31