You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:21:55