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キーを2回押した後でのみ、「>>」が出力されます。ここで何が起こっているのか理解できません。私のコードに対する他の改善は大歓迎です。


編集::getCharの代わりにgetLineを使用することにより、プログラムは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}

入力を受け取った後に「>>」とgetDataが出力されるのはなぜputStrですか?

4

1 に答える 1

4
option <- getChar

デフォルトのバッファリング設定では、プログラムは改行が入力された後にのみ入力を受け取ります。ただし、はgetChar入力バッファーから入力された最初の文字のみを削除するため、次getLineのコードは改行が見つかるまで入力バッファーから読み取ります。あなたの場合、最初にすぐに。

(少なくとも *nix っぽいシステムでは、以前は Windows で適切に機能しなかったバッファリング制御が、現在機能しているかどうかはわかりません) のバッファリングをオフにすることで問題を解決できますstdin

main = do
    hSetBuffering stdin NoBuffering
    ...

そのため、getChar改行を入力せずに入力を受け取ります。getCharまたは、オプションに を使用する代わりに、

(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 に答える