Vid*_*hra 4 haskell eclipse-fp
我正在尝试使用交互功能,但我遇到以下代码的问题:
main::IO()
main = interact test
test :: String -> String
test [] = show 0
test a = show 3
Run Code Online (Sandbox Code Playgroud)
我正在使用EclipseFP并且输入一个输入似乎有错误.尝试再次运行main会导致:
*** Exception: <stdin>: hGetContents: illegal operation (handle is closed)
Run Code Online (Sandbox Code Playgroud)
我不确定为什么这不起作用,测试类型是String - > String并且show是Show a => a - > String,所以看起来它应该是一个有效的交互输入.
编辑/ UPDATE
我尝试了以下,它工作正常.如何使用unlines和lines导致交互按预期工作?
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
Run Code Online (Sandbox Code Playgroud)
您可以将此问题(例外)减少为:
main = getContents >> return ()
Run Code Online (Sandbox Code Playgroud)
(interact电话getContents)
问题是stdin(getContents实际上hGetContents stdin)仍然在GHCi之间进行评估main.如果你抬头看stdin,它实现为:
stdin :: Handle
stdin = unsafePerformIO $ ...
Run Code Online (Sandbox Code Playgroud)
要了解为什么这是一个问题,您可以将其加载到GHCi中:
import System.IO.Unsafe
f :: ()
f = unsafePerformIO $ putStrLn "Hi!"
Run Code Online (Sandbox Code Playgroud)
然后,在GHCi中:
*Main> f
Hi!
()
*Main> f
()
Run Code Online (Sandbox Code Playgroud)
由于我们已经使用unsafePerformIO并告诉编译器这f是一个纯函数,因此它认为它不需要再次对它进行求值.在这种情况下stdin,句柄上的所有初始化都不会再次运行,并且它仍然处于半封闭状态(将其hGetContents置于其中),这会导致异常.所以我认为GHCi在这种情况下是"正确的",问题在于定义stdin哪个是编译程序的实际便利,它只会评估stdin一次.
至于为什么interact在unlines . lines版本继续之后退出单行输入的原因,让我们尝试减少它:
main :: IO ()
main = interact (const "response\n")
Run Code Online (Sandbox Code Playgroud)
如果您测试上述版本,则交互在打印前甚至不会等待输入response.为什么?这是interact(在GHC中)的来源:
interact f = do s <- getContents
putStr (f s)
Run Code Online (Sandbox Code Playgroud)
getContents是惰性I/O,因为f在这种情况下不需要s,所以不会读取任何内容stdin.
如果您将测试程序更改为:
main :: IO ()
main = interact test
test :: String -> String
test [] = show 0
test a = show a
Run Code Online (Sandbox Code Playgroud)
你应该注意到不同的行为.这表明在原始版本(test a = show 3)中,编译器足够聪明,可以意识到它只需要足够的输入来确定字符串读取是否为空(因为如果它不是空的,则不需要知道什么a是,它只需要打印"3").由于输入可能是在终端上进行行缓冲,因此在您按下返回键之前会一直读取.
| 归档时间: |
|
| 查看次数: |
650 次 |
| 最近记录: |