OCaml实现连续子串首尾字符位置函数及类型错误排查
解决你的OCaml连续子串位置匹配问题
首先来看你遇到的错误:The variable h1 on the left-hand side of this or-pattern has type 'a but on the right-hand side it has type 'a list,这个问题出在aux函数的模式匹配分支里:
| h1::q1, h2 | h1, h2 -> if h1 = h2 then x else -1
这里的核心问题是或模式两边的h1类型完全不一致:左边的h1是列表的单个元素(类型'a),而右边的h1是整个列表(类型'a list),OCaml的强类型系统不允许这种矛盾的类型定义,所以抛出了类型错误。
接下来我们重新梳理逻辑,你的需求很明确:
- 若目标子串是原字符串的连续子序列,返回它的首尾字符索引(从0开始计数,比如
"tar"里的"ar"对应索引1和2) - 若找不到连续匹配的子串,返回类似
Nil的结果(我们可以用OCaml原生的option类型,或者自定义类型来实现)
修正后的完整代码(用option类型表示结果)
(* 先将字符串转为字符列表,方便递归逐个匹配 *) let my_sub s sub = let rec pos_sub chars sub_chars start_idx = (* aux函数:检查从当前位置开始,能否连续匹配完整个子串,返回结束索引(成功则返回Some,失败返回None) *) let rec aux remaining_chars remaining_sub current_idx = match remaining_chars, remaining_sub with | _, [] -> Some current_idx (* 子串全部匹配完成,返回当前结束索引 *) | [], _ -> None (* 原串已遍历完,子串还没匹配完,失败 *) | h1::q1, h2::q2 -> if h1 = h2 then aux q1 q2 (current_idx + 1) else None (* 当前字符不匹配,直接宣告失败 *) in match chars, sub_chars with | [], _ -> None (* 原串为空,直接返回失败 *) | _::_, [] -> None (* 子串为空,按需求返回失败(可根据需要调整) *) | h1::q1, h2::q2 -> if h1 = h2 then (* 当前字符匹配,调用aux检查后续是否能连续匹配完子串 *) match aux q1 q2 (start_idx + 1) with | Some end_idx -> Some (start_idx, end_idx) | None -> pos_sub q1 sub_chars (start_idx + 1) (* 此处匹配失败,继续往后查找 *) else pos_sub q1 sub_chars (start_idx + 1) (* 当前字符不匹配,直接往后移动起始位置 *) in (* 转换字符串为字符列表,从索引0开始查找 *) pos_sub (String.to_list s) (String.to_list sub) 0
测试示例
(* 测试1:匹配成功 *) my_sub "tar" "ar";; (* 返回 Some (1, 2),和你的示例完全一致 *) (* 测试2:子串非连续,匹配失败 *) my_sub "tabr" "ar";; (* 返回 None,对应你需求中的Nil *) (* 额外测试:子串在原串开头 *) my_sub "hello" "he";; (* 返回 Some (0,1) *) (* 额外测试:子串不存在于原串 *) my_sub "hello" "xyz";; (* 返回 None *)
若你需要自定义Nil类型(而非option)
可以先定义一个结果类型,再调整返回值:
type match_result = Nil | Pos of int * int let my_sub s sub = let rec pos_sub chars sub_chars start_idx = let rec aux remaining_chars remaining_sub current_idx = match remaining_chars, remaining_sub with | _, [] -> Some current_idx | [], _ -> None | h1::q1, h2::q2 -> if h1 = h2 then aux q1 q2 (current_idx +1) else None in match chars, sub_chars with | [], _ -> Nil | _::_, [] -> Nil | h1::q1, h2::q2 -> if h1 = h2 then match aux q1 q2 (start_idx +1) with | Some end_idx -> Pos (start_idx, end_idx) | None -> pos_sub q1 sub_chars (start_idx +1) else pos_sub q1 sub_chars (start_idx +1) in pos_sub (String.to_list s) (String.to_list sub) 0
测试这个版本:
my_sub "tar" "ar";; (* 返回 Pos (1,2) *) my_sub "tabr" "ar";; (* 返回 Nil *)
这样就完全符合你的需求了。
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

