如何在Haskell中正确定义可选参数?以Collatz猜想递归函数实现为例
你代码的核心问题有三个
- 类型匹配错误:你声明的第二个参数
count是Maybe Integer类型,但是你在代码中直接将其作为Integer执行+1运算、用Just count返回,相当于给已经是Maybe类型的值又套了一层Maybe,所以编译器会提示类型不匹配,期望是Maybe Integer实际得到的是Maybe (Maybe Integer)。 - 模式匹配语法错误:你写的
collatz v () = collatz v 0中的()是Unit类型,和你签名里的第二个参数Maybe Integer完全不属于同一类型,永远不可能被匹配到,你想要模拟JS的缺省参数逻辑在Haskell里不能这么实现。 - 对外接口不符合需求:你现在的类型签名是接受两个参数,不符合你「仅需传入序列起始数字」的要求。
正确实现思路
Haskell中实现这种需要内部隐式计数器的递归函数,标准做法是拆分对外暴露的接口函数和内部递归辅助函数,对外的函数只接受起始数字,调用内部辅助函数时自动传入初始计数器0即可,还可以在对外接口层统一做输入合法性校验(Collatz猜想仅对正整数有效,小于等于0的输入直接返回Nothing)。
正确代码示例
module CollatzConjecture (collatz) where -- 对外接口:仅接受起始数字一个入参 collatz :: Integer -> Maybe Integer collatz v | v <= 0 = Nothing -- 处理非法输入 | otherwise = Just $ recursiveGo v 0 -- 调用内部递归函数,初始计数器置0 where -- 内部递归辅助函数:不需要处理Maybe,仅做步数计算 recursiveGo 1 count = count recursiveGo n count | even n = recursiveGo (n `div` 2) (count + 1) | otherwise = recursiveGo (n * 3 + 1) (count + 1)
补充(不推荐):沿用双参数逻辑的修正写法
如果一定要沿用你原有的双参数写法,修正后的代码如下,但这种写法会对外暴露计数器参数,调用时需要传入collatz 6 Nothing才能得到结果,不符合你的需求:
module CollatzConjecture (collatz) where collatz :: Integer -> Maybe Integer -> Maybe Integer -- 用Nothing匹配缺省传入的第二个参数 collatz v Nothing = collatz v (Just 0) collatz 1 (Just count) = Just count collatz v (Just count) | even v = collatz (div v 2) next | otherwise = collatz (v * 3 + 1) next where next = Just (count + 1)
内容的提问来源于stack exchange,提问作者Andreas Bolz
相关产品推荐
相关产品推荐

