34

私は Haskell の初心者です。モナドについて頭を包み始めたばかりですが、まだよくわかりません。ユーザーに入力を求めて応答するゲームを書いています。これが私の関数の簡略化されたバージョンです:

getPoint :: IO Point
getPoint = do
    putStr "Enter x: "
    xStr <- getLine
    putStr "Enter y: "
    yStr <- getLine
    return $ Point (read xStr) (read yStr)


completeUserTurn :: (Board, Player) -> IO (Board, Player)
completeUserTurn (board, player) = do
    putStr $ "Enter some value: "
    var1 <- getLine
    putStr $ "Enter another value: "
    var2 <- getLine
    putStr $ "Enter a point this time: "
    point <- getPoint
    if (... the player entered legal values ...) then do
        putStr $ "This is what would happen if you did that: {stuff} do you want to do that? (y/n) "
        continue <- getLine
        if continue == "y" then
            return (...updated board..., ...updated player...)
        else
            completeUserTurn (board, player)
    else do
        putStr "Invalid Move!\n"
        completeUserTurn (board, player)

何が起こっているかというと、プロンプトが、プロンプトの前に表示されるはずのテキストで順不同に表示されるということです。

上記のコードをコンパイルした後の例を次に示します。

1
値を入力してください: 別の値を入力してください: 2
3
4
今回の点を入力してください: x を入力してください: y を入力してください: y
でよろしいですか? (y/n):

太字は私が入力したものです。

明らかに、いくつかの大きな概念上のエラーがありますが、何が原因かわかりません。インタープリターでは正しく機能し、コンパイルすると失敗することに注意してください。

4

3 に答える 3

59

マイケルが言ったように、問題はバッファリングです。デフォルトでは、出力は改行を出力するまで (または非常に長い行がある場合はバッファーがいっぱいになるまで) バッファーに入れられるため、同じ行のプロンプトを実行しようとすると、この問題が最も頻繁に発生しputStrます。

フラッシュを処理するために、次のような小さなヘルパー関数を定義することをお勧めします。

import System.IO

prompt :: String -> IO String
prompt text = do
    putStr text
    hFlush stdout
    getLine

今、あなたは簡単に行うことができます

getPoint = do
    xStr <- prompt "Enter x: "
    yStr <- prompt "Enter y: "
    return $ Point (read xStr) (read yStr)
于 2012-11-02T07:11:00.170 に答える
17

IOは正しい順序で発生しています。問題はバッファリングです。各putStrの後にstdoutをフラッシュすると、期待どおりに機能するはずです。からインポートする必要がありhFlushます。stdoutSystem.IO

于 2012-11-02T06:56:02.047 に答える
12

問題は、IO コードの操作の順序にはありませんでした。問題は入力であり、stdin と stdout を使用する場合、出力はデフォルトでバッファリングされます。これにより、アプリでの IO のパフォーマンスが向上しますが、stdin と stdout の両方が使用されている場合、操作が順不同で発生するように見える可能性があります。

これには 2 つの解決策があります。このメソッドを使用してhFlush、ハンドル (stdin または stdout) を強制的にフラッシュすることができます。例えばhFlush stdouthFlush stdin。より簡単な解決策 (対話型アプリでは問題なく機能します) は、バッファリングを完全に無効にすることです。hSetBuffering stdout NoBufferingメソッドを呼び出してhSetBuffering stdin NoBuffering、プログラムを開始する前にこれを行うことができます (つまり、これらの行をメイン メソッドに入れます。

于 2012-11-02T07:07:32.517 に答える