4

対話機能を使用しようとしていますが、次のコードに問題があります。

main::IO()
main = interact test

test :: String -> String
test [] = show 0
test a = show 3

私は EclipseFP を使用していますが、エラーがあるように見えます。main を再度実行しようとすると、次のようになります。

*** Exception: <stdin>: hGetContents: illegal operation (handle is closed)

これが機能しない理由はわかりません。テストのタイプは String -> String で、show は Show a => a -> String であるため、interact の有効な入力である必要があるようです。

編集/更新

私は以下を試しましたが、うまくいきます。unlines と lines を使用すると、interact はどのように期待どおりに動作しますか?

main::IO()
main = interact respondPalindromes

respondPalindromes :: String -> String
respondPalindromes =
    unlines .
    map (\xs -> if isPal xs then "palindrome" else "not a palindrome") .
    lines

isPal :: String -> Bool
isPal xs = xs == reverse xs
4

1 に答える 1

7

GHCi と安全でない I/O

この問題 (例外) を次のように減らすことができます。

main = getContents >> return ()

interactコールgetContents

問題は、stdin(getContents実際にはhGetContents stdin) が GHCi の呼び出しの間に評価されたままになることmainです。を調べるとstdin、次のように実装されています。

stdin :: Handle
stdin = unsafePerformIO $ ...

これが問題である理由を確認するには、これを GHCi にロードします。

import System.IO.Unsafe                                                                                                           

f :: ()                                                                                                                           
f = unsafePerformIO $ putStrLn "Hi!"

次に、GHCiで次のようにします。

*Main> f
Hi!
()
*Main> f
()

純粋な関数を使用unsafePerformIOしてコンパイラに伝えたので、コンパイラfはそれを再度評価する必要はないと考えます。の場合stdin、ハンドルのすべての初期化が 2 回目に実行されることはなく、まだ半閉じた状態 (ハンドルをhGetContents入れる) であるため、例外が発生します。したがって、この場合、GHCiは「正しい」と思います。問題は、一度stdinだけ評価されるコンパイル済みプログラムにとって実用的な便利さの定義にありstdinます。

対話と遅延 I/O

interactバージョンが続行している間に 1 行の入力後に終了する理由についてunlines . linesは、それも減らしてみましょう。

main :: IO ()
main = interact (const "response\n")

上記のバージョンをテストすると、interact は印刷する前に入力を待つことさえしませんresponse。なんで?interact(GHCの)ソースは次のとおりです。

interact f = do s <- getContents
                putStr (f s)

getContentsは遅延 I/O であり、fこの場合は を必要としないためs、 からは何も読み取られませんstdin

テスト プログラムを次のように変更した場合:

main :: IO ()
main = interact test

test :: String -> String
test [] = show 0
test a = show a

異なる動作に気付くはずです。そして、これは、元のバージョン ( test a = show 3) では、コンパイラは、読み取った文字列が空かどうかを判断するのに十分な入力のみが必要であることを認識するのに十分スマートであることを示唆しています (空でない場合は、何が何でaあるかを知る必要がないため)。 、印刷するだけ"3"です)。入力はおそらく端末上で行バッファリングされているため、リターンキーを押すまで読み取ります。

于 2013-01-27T10:12:35.263 に答える