使用 IO.readLn 从 Haskell 中的标准输入读取单行
Posted
技术标签:
【中文标题】使用 IO.readLn 从 Haskell 中的标准输入读取单行【英文标题】:Read single line from stdin in Haskell using IO.readLn 【发布时间】:2011-12-09 17:26:24 【问题描述】:此代码在 GHC 7.0.3 中无法编译:
import System.IO
main = do
z <- readLn
print z
我的意图是从标准输入读取一行并将其存储在 z 中,以便以后用它做更高级的事情。错误信息如下:
test.hs:5:9:
Ambiguous type variable `a0' in the constraints:
(Show a0) arising from a use of `print' at test.hs:5:9-13
(Read a0) arising from a use of `readLn' at test.hs:4:14-19
Probable fix: add a type signature that fixes these type variable(s)
In a stmt of a 'do' expression: print z
In the expression:
do z <- readLn;
print z;
return ()
In an equation for `main':
main
= do z <- readLn;
print z;
return ()
显然有一些基本的东西我还没有理解;请向我解释为什么它不起作用以及如何解决它。
EDIT1:我通过将print z
更改为putStrLn z
修复了编译错误,因此GHC 知道我想读取一个字符串。但是当我运行程序时,我得到一个我无法理解的运行时错误:
$ ./test
hello!
test: user error (Prelude.readIO: no parse)
$
我刚刚输入了“你好!”然后进入。请注意,我在 OS X 上运行 x86_64 GHC,这被认为是不稳定的。
EDIT2:我将 readLn 更改为 getLine,它无缘无故地神奇地工作。我想知道为什么,但我很高兴它有效。
最终代码:
import System.IO
main = do
z <- getLine
print z
【问题讨论】:
getLine 导致 readIO: no parse 错误的原因是使用 String 时的 read 函数期望字符串为 haskell 字符串格式。例如在 ghci 中:read "test" :: String
将导致错误,但 read "\"test\"" :: String
将返回 "test"
。如果你包围你好,你的第一个程序会工作!用双引号。如果您只想要任何字符串,请始终使用 getLine
而不是 readLn
。
【参考方案1】:
readLn 作为类型:Read a => IO a
。它从用户那里读取一行,然后将字符串解析为类型a
。 a
是什么类型?它是你想要的任何东西(只要它是Read
的一个实例)。例如:
readAInt :: IO Int
readAInt = readLn
readABool :: IO Bool
readABool = readLn
print
的类型为 Show a => a -> IO ()
。它采用Show
的实例类型,并将其打印出来。例如,要打印True
,您可以使用print True
。要打印 Int 42,可以使用print 42
。
在您的示例中,您同时使用 print 和 readLn。这不起作用,因为 haskell 无法确定 readLn
应该返回什么类型。 print
可以采用任何可显示的类型,因此它不限于将返回的类型。这使得readLn
的返回类型模棱两可,因为haskell 无法确定类型。这就是错误消息的意思。
你可能只存储用户输入的字符串,而不是将它读入你自己的类型。您可以使用类型为getLine :: IO String
的getLine 执行此操作。同样,您可以使用putStrLn
而不是print
来打印一个字符串。 putStrLn
的类型为 String -> IO ()
。
【讨论】:
【参考方案2】:这就是您将代码更改为的内容,对吧?
import System.IO
main = do
z <- readLn
putStrLn z
putStrLn
将String
写入标准输出,因此z
是String
。因此readLn
将从标准输入中读取String
。
但是...readLn
期望从标准输入读取 Haskell 格式的值。即,它不希望您输入类似 This is a string
的内容,而是将其放在引号中:"This is a string"
。
要解决此问题,请将 readLn
替换为 getLine
,它读取的是文字文本,而不是 Haskell 格式的字符串。
import System.IO
main = do
z <- getLine
putStrLn z
【讨论】:
【参考方案3】:readLn 读回您指定的类型,因此不能以这种方式使用:它需要在指定其类型的函数中使用。另一方面,getLine 总是返回一个字符串,所以它会做你想做的事。
值得注意的是,您可能还想使用 putStrLn 而不是 print; print 会加引号。
因此 GHCi 中的 do z <- getLine; putStrLn z;
应该做你想做的事。
【讨论】:
以上是关于使用 IO.readLn 从 Haskell 中的标准输入读取单行的主要内容,如果未能解决你的问题,请参考以下文章
将抽象数据类型保存和加载到 json 文件并从游戏中的文件中读取 Haskell
Haskell Gloss,在动画功能中从控制台读取不会更新绘图