OCaml递归函数栈溢出(循环递归)及类型标注括号问题
栈溢出问题定位
left_comps无限递归触发栈溢出的核心原因是缺少长度为1的列表的终止分支:
当传入列表长度为1时,依然会匹配到s::t分支(此时t为空列表):
- 调用
nth t 0时,因为你的nth函数对空列表输入默认返回0,因此得到计算值s + 0 = s - 调用
no_first t时,空列表输入返回空列表 - 最终计算出的
x就是s :: [],和传入的原列表完全一致
后续递归调用left_comps x等价于再次传入相同参数,永远无法终止,最终耗尽栈空间。
另外你的实现存在几个额外问题:
nth t 0本质是取列表首元素,不需要递归实现,空列表场景应该直接走终止逻辑,不应该默认返回0no_first t本质是取列表尾,可以直接通过模式匹配拿到,不需要单独封装- 原
left_comp_once没有处理长度为1的输入,会生成错误结果 - 原
left_comps没有把初始输入列表加入结果集,就算修复递归终止问题,输出也会缺少第一个序列。
类型标注括号的作用
OCaml中列表类型构造器list是右结合的,int list -> int list list和你期望的int list -> (int list) list是完全等价的,括号不改变类型语义,仅用于提升可读性:(int list) list可以更清晰地表达“元素类型为int列表的列表”,避免阅读时产生优先级误解,编译器类型推导时不会因为加不加这组括号生成不同的类型。
修正后代码
移除冗余的辅助函数,补上递归终止条件即可,代码如下:
let rec left_comps l = match l with | [] -> [[]] | [_] as single -> [single] (* 列表只剩1个元素时终止递归 *) | a::b::rest -> let next_step = (a + b) :: rest in l :: left_comps next_step (* 收集当前步结果,递归计算下一轮 *)
执行left_comps [1; 2; 0; 4; 2; 1]会得到和预期完全一致的输出:
- : int list list = [[1; 2; 0; 4; 2; 1]; [3; 0; 4; 2; 1]; [3; 4; 2; 1]; [7; 2; 1]; [9; 1]; [10]]
内容的提问来源于stack exchange,提问作者TheWorstProgrammer
相关产品推荐
相关产品推荐

