Let语句内部的Irrefutable模式失败

时间:2013-02-03 06:59:32

标签: haskell design-patterns let

我正在学习haskell,目前正在尝试从字符串中解析Integers和Floats。

然而,当我在“342”或任何没有单个或多个非数字字符的“数字”上尝试我的readNum函数时,ghci向我报告:

  

* 异常:parse.hs:125:18-46:模式的无可辩驳模式失败(i,(a    :as))

data Token
    = IntTok Int | FloatTok Float | EOF

readNum :: String->(Token, String)
readNum [] = (EOF, [])
readNum xs = let (i, (a:as)) = span isDigit xs   --This is line 125
                in (case a of
                        ('.') -> let (j, (b:c:bs)) = span isDigit as
                                        in (if ((toLower b) == 'e' && (c == '+' || c == '-' || (isDigit c)))
                                                then (let (k, d) = span isDigit bs in (FloatTok (read (concat [i,[a],j, [b],[c],k])::Float), d))
                                                else (FloatTok (read (concat [i,[a],j])::Float), (b:c:bs)))
                        _ -> (IntTok (read i::Int), (a:as)))

span isDigit xs返回空列表作为元组的第二个元素时,是否有更好的方法来处理这种情况?

-Thanks

1 个答案:

答案 0 :(得分:6)

您收到错误是因为如果您使用"342"之类的简单整数,那么span isDigit "342"只是("342",[]),而(l,a:as)无法匹配。应该始终匹配的模式称为无可辩驳模式。正如您所发现的那样,let绑定中的模式是无可辩驳的,所以......

您需要坚持在let绑定中始终匹配的模式。例如,你可以做

readNum xs = let (i, ps) = span isDigit xs 
             in (case ps of 
                     ('.':as) -> let (j, qs) = span isDigit as in case qs of
                           b:c:bs -> if  ..........
                           _ -> error "not enough stuff after decimal number"
                     _ -> .... 

我给出了一个愚蠢的错误信息,但很明显你应该在那里写出更明智的代码。