Haskell中chainl1与中缀操作符结合使用的异常问题咨询
问题分析与解决
首先明确代码的核心问题:
- 符号匹配错误:
pOper里的symbol "\"+\""是匹配带双引号的"+"字符串,但输入"2+2"里的+没有引号,导致运算符完全匹配失败,解析器返回空列表[]。 - 你担心的匿名函数类型问题其实不成立:
(\e1 e2 -> Not (Oper Eq e1 e2))的类型确实是Exp -> Exp -> Exp,符合pOper的类型签名——因为Not Exp属于Exp类型,这个匿名函数接收两个Exp参数并返回一个Exp,类型完全合法。
以下是保留pOper类型签名、不修改类型定义的修复方案,重点修正符号匹配逻辑并优化空格处理:
import Data.Char import Text.ParserCombinators.ReadP import Control.Applicative ((<|>)) type Parser a = ReadP a data Value = IntVal Int deriving (Eq, Show, Read) data Exp = Const Value | Oper Op Exp Exp | Not Exp deriving (Eq, Show, Read) data Op = Plus | Minus | Eq deriving (Eq, Show, Read) space :: Parser Char space = satisfy isSpace spaces :: Parser String spaces = many space space1 :: Parser String space1 = many1 space symbol :: String -> Parser String symbol = token . string -- 优化token:解析前后都跳过空格,自动处理运算符前后的空白 token :: Parser a -> Parser a token combinator = do spaces result <- combinator spaces return result parseString input = readP_to_S (do e <- pExpr token eof return e) input pExpr :: Parser Exp pExpr = chainl1 pTerm pOper -- 简化pTerm:numConst已包含空格处理,无需重复逻辑 pTerm :: Parser Exp pTerm = numConst numConst :: Parser Exp numConst = do skipSpaces y <- munch isDigit return (Const (IntVal (read y))) -- 修改运算符匹配逻辑:去掉符号的引号,直接匹配裸运算符 pOper :: ReadP (Exp -> Exp -> Exp) pOper = symbol "+" >> return (Oper Plus) <|> symbol "-" >> return (Oper Minus) <|> symbol "=" >> return (Oper Eq) <|> symbol "!=" >> return (\e1 e2 -> Not (Oper Eq e1 e2))
测试验证:
- 输入
parseString "2+2"会返回[(Oper Plus (Const (IntVal 2)) (Const (IntVal 2)), "")],符合预期。 - 输入
parseString "2!=3"会返回[(Not (Oper Eq (Const (IntVal 2)) (Const (IntVal 3))), "")],正确解析不等于运算。
内容的提问来源于stack exchange,提问作者Piskator
相关产品推荐
相关产品推荐

