Haskell字符串转整数解析器异常处理问题排查
Haskell 字符串转整数Parser实现问题修复
核心问题1:nat解析器无法拦截前导零的原因
你的nat实现判断逻辑存在遗漏,同时对many1的匹配行为和read函数的特性理解有偏差:
- 你仅判断了匹配到的数字串是否完全等于单个字符"0",没有覆盖「数字串以'0'开头且长度大于1」的前导零非法场景。
- 你当前实现的
<|>是左偏最长匹配逻辑,many1 digit会一次性消耗输入开头所有连续数字,返回最长匹配结果,不会逐字符返回短匹配:- 输入
"000def"时,many1 digit匹配到最长串"000",这个串不等于"0",因此走到else分支调用read "000"——Haskell的read会自动忽略数字串前导零,read "000"的计算结果就是0,因此得到输出[(0, "def")]。 - 输入
"012def"时,many1 digit匹配到最长串"012",不等于"0",read "012"得到12,因此输出[(12, "def")]。
- 输入
正确的nat实现
不需要匹配完数字串再做判断,直接通过组合子约束合法数字的格式:合法自然数的第一个字符必须是1-9的非零数字,后面可以跟任意数量0-9的数字,从根源上避免前导零和单独零的问题:
-- 匹配1-9的非零数字 nonZeroDigit :: Parser Char Char nonZeroDigit = sat (\c -> c >= '1' && c <= '9') nat :: Parser Char Int nat = nonZeroDigit >>= \first -> many digit >>= \rest -> return (read (first:rest))
该实现的测试结果完全符合预期:
nat "0" -- > [] 单个零非法 nat "0def" -- > [] 前导零非法 nat "012def" -- > [] 前导零非法 nat "000def" -- > [] 前导零非法 nat "123def" -- > [(123, "def")] 合法正整数 nat "100def" -- > [(100, "def")] 数字末尾/中间的零合法
核心问题2:int解析器的负号支持实现
你之前写的两个int版本都存在本质错误:
- 第一版完全没有处理
'-'字符的匹配逻辑,且nat返回值永远是正整数,s < 0的判断永远不成立。 - 第二版依然用
many1 digit做匹配,永远捕获不到'-'字符,且return (read s) ++ "-"存在类型错误,完全无法通过编译。
正确的int实现
int的合法格式只有两种,直接基于已实现的nat做组合即可,完全复用nat的校验逻辑:
- 直接匹配一个合法nat,对应正整数
- 先匹配一个
'-'字符,再匹配一个合法nat,将结果取反对应负整数
首先补充负号匹配器,再实现int:
-- 匹配负号 negSign :: Parser Char Char negSign = sat (== '-') int :: Parser Char Int int = nat <|> (negSign >> nat >>= \n -> return (-n))
该实现的测试结果符合预期:
int "123def" -- > [(123, "def")] 正整数解析正确 int "-123def" -- > [(-123, "def")] 负整数解析正确 int "-012def" -- > [] 带前导零的负数非法 int "-0def" -- > [] -0非法 int "0def" -- > [] 零非法
补充:缺失的类型类实例
你当前的代码直接使用了>>=、return等Monad操作,但没有为Parser类型定义对应的类型类实例,在新版本GHC中无法通过编译,需要补充以下代码:
instance Functor (Parser tok) where fmap f p = p >>= \v -> return (f v) instance Applicative (Parser tok) where pure = Main.return pf <*> pv = pf >>= \f -> pv >>= \v -> return (f v) instance Monad (Parser tok) where p >>= f = \ts -> concat [f v rest | (v, rest) <- p ts]
内容的提问来源于stack exchange,提问作者love Croquembouch
相关产品推荐
相关产品推荐

