Haskell实现Ackermann函数触发Parse error in pattern:a-1报错
Haskell实现Ackermann函数报错修复
报错触发原因
你遇到的Parse error in pattern: a - 1解析错误,直接触发点是代码里return ak(a-1, 1)这行的写法不符合Haskell语法规则:
Haskell中函数应用优先级最高、且遵循左结合规则,你写的return ak(a-1, 1)会被编译器解析为(return ak) (a-1, 1)——也就是先把ak作为return的参数传入,再把(a-1, 1)作为前者返回值的参数。编译器解析这个结构时,会误将a-1识别为模式匹配片段,直接抛出解析错误。
这个错误的根源是你把纯计算的Ackermann函数错误套入了IO Monad的do语法结构,额外引入了不必要的语法约束,除此之外代码里还存在几个隐性问题:
- 类型不匹配:main函数中
n和m是read读入的数值类型,直接用++和字符串拼接会触发类型错误,需要先通过show转为字符串。 - 递归逻辑错误:你写的非零分支递归逻辑不符合Ackermann函数的标准定义,运行后会得到错误结果。
- 冗余语法:Ackermann函数是无副作用的纯数值计算,不需要
do块、return包装,这类语法是给IO这类带上下文的Monadic操作使用的。
修复后可运行代码
main :: IO () main = do print "Please enter first operand: " input <- getLine let n = read input :: Int print "Please enter second operand: " input <- getLine let m = read input :: Int let r = ak n m print $ show n ++ " ackermann " ++ show m ++ " = " ++ show r -- 纯函数实现,无需do/return包装 ak :: Int -> Int -> Int ak 0 b = b + 1 ak a 0 = ak (a - 1) 1 ak a b = ak (a - 1) (ak a (b - 1))
修复说明
- 给
read增加了明确的Int类型标注,避免编译器类型推断出现歧义 - 移除了
ak函数上冗余的do块和return包装,采用Haskell惯用的模式匹配方式编写递归分支,符合纯函数写法规范 - 修正了递归分支的计算逻辑,对齐Ackermann函数的标准定义
- 输出拼接时对数值类型的
n、m先用show做类型转换,解决类型不匹配问题 - 将类C语言的
f(x,y)元组传参写法改为Haskell原生的柯里化传参f x y,从根源上避免函数优先级导致的解析错误
注意:Ackermann函数增长速度极快,输入参数大于3后计算量会呈爆炸式增长,测试时不要输入过大参数,避免程序长时间占用资源无响应。
内容的提问来源于stack exchange,提问作者Elliot
相关产品推荐
相关产品推荐

