读取字符串并测试它是否为数字

时间:2011-05-21 19:41:12

标签: parsing haskell io

为什么这段代码不起作用?如果字符串是数字,我想返回Bool。

isNumber = do
    n <- getLine
    let val = case reads n of
                ((v,_):_) -> True
                _ -> False

2 个答案:

答案 0 :(得分:16)

首先,您遇到语法错误:

A.hs:3:5:
    The last statement in a 'do' construct must be an expression:
    let val
          = case reads n of {
              ((v, _) : _) -> True
              _ -> False }

因为您的函数尚未返回值。修复:

isNumber = do
    n <- getLine
    let val = case reads n of
                ((v,_):_) -> True
                _         -> False
    return val

现在它在语法上是正确的,但它有一个类型错误:

A.hs:3:20:
    Ambiguous type variable `a0' in the constraint:
      (Read a0) arising from a use of `reads'
    Probable fix: add a type signature that fixes these type variable(s)

为什么呢?因为read超载了。所以编译器不知道你想要读什么。在这种情况下,您正在尝试读取数字。比方说,Integer

isNumber :: IO Bool
isNumber = do
    n <- getLine
    let val = case (reads :: ReadS Integer) n of
                ((v,_):_) -> True
                _         -> False
    return val

尽管如此,它并不是真正的惯用语。让我们将IO与纯代码分开,并实际返回解析后的数字,如果成功的话:

readNumber :: String -> Maybe Integer
readNumber s = case reads s of
            ((v,_):_) -> Just v
            _         -> Nothing

getNumber :: IO (Maybe Integer)
getNumber = do
    s <- getLine
    return (readNumber s)

测试:

*Main> getNumber 
123
Just 123
*Main> getNumber 
dons
Nothing

因此我们已经清理了解析,并将IO从解析中分离出来,这意味着您可以单独测试解析器,并添加类型信息来记录您的设计。

答案 1 :(得分:0)

添加return val或只写return $ case ...do ...中的最后一个语句必须是表达式。在您的特定情况下,它必须是IO Bool类型的表达式,因此您需要使用return函数将值提升到IO monad中。您还需要明确指定v的类型(您可能需要ScopedTypeVariables GHC扩展名。)

编写一个单独的String -> Bool类纯函数也是一个好主意,并在不纯的IO代码中使用它。