使用 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 =&gt; IO a。它从用户那里读取一行,然后将字符串解析为类型aa 是什么类型?它是你想要的任何东西(只要它是Read 的一个实例)。例如:

readAInt :: IO Int
readAInt = readLn

readABool :: IO Bool
readABool = readLn

print 的类型为 Show a =&gt; a -&gt; IO ()。它采用Show 的实例类型,并将其打印出来。例如,要打印True,您可以使用print True。要打印 Int 42,可以使用print 42


在您的示例中,您同时使用 print 和 readLn。这不起作用,因为 haskell 无法确定 readLn 应该返回什么类型。 print 可以采用任何可显示的类型,因此它不限于将返回的类型。这使得readLn 的返回类型模棱两可,因为haskell 无法确定类型。这就是错误消息的意思。


你可能只存储用户输入的字符串,而不是将它读入你自己的类型。您可以使用类型为getLine :: IO String 的getLine 执行此操作。同样,您可以使用putStrLn 而不是print 来打印一个字符串。 putStrLn 的类型为 String -&gt; IO ()

【讨论】:

【参考方案2】:

这就是您将代码更改为的内容,对吧?

import System.IO

main = do
    z <- readLn
    putStrLn z

putStrLnString 写入标准输出,因此zString。因此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 &lt;- getLine; putStrLn z; 应该做你想做的事。

【讨论】:

以上是关于使用 IO.readLn 从 Haskell 中的标准输入读取单行的主要内容,如果未能解决你的问题,请参考以下文章

将抽象数据类型保存和加载到 json 文件并从游戏中的文件中读取 Haskell

Haskell,树中的列表列表

Scala中的Haskell“forall”翻译

Haskell Gloss,在动画功能中从控制台读取不会更新绘图

从 Haskell 翻译的 Python 中,Count 懒惰地运行

Haskell 中的模块化算术