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

如何让Coq接受归纳类型与nat的互递归定义?

这事儿我熟!Coq的默认终止检查器对互递归函数确实有个“死板”的限制——它要求所有互递归函数的主递归参数得属于同一个归纳类型,但你的evalT主参数是自定义的T类型,evalBar的主参数是nat,俩类型不搭,所以就触发了报错。不过放心,有好几种办法能让Coq接受这个定义,给你列几个常用的:

方案1:用Program Fixpoint手动指定终止度量

Program Fixpoint允许你给每个函数指定一个“终止度量”,简单说就是一个能衡量参数“大小”的数值,你只需要证明每次递归调用的度量都会严格变小,Coq就会认可函数的终止性。

示例代码:

Require Import Program.Wf.
Require Import Arith.

Inductive T := | foo : T | bar : nat -> T -> T.

Program Fixpoint evalT (t:T) {measure (size t)} : nat :=
  match t with
  | foo => 1
  | bar n x => evalBar x n
  end
with evalBar (x:T) (n:nat) {measure n} : nat :=
  match n with
  | O => 0
  | S n' => (evalT x) + (evalBar x n')
  end.
Next Obligation.
  (* 证明evalT x的参数比evalT (bar n x)的参数结构更小 *)
  simpl; apply lt_S_n.
Qed.
Next Obligation.
  (* 证明evalBar x n'的参数n'比n更小 *)
  apply lt_n_S.
Qed.
Next Obligation.
  (* 确认nat的小于关系是良基的(Coq自动能处理) *)
  apply wf_nat.
Qed.

方案2:把互递归改成单递归(最简洁)

其实你可以把evalBar作为evalT的局部辅助函数,这样就避开了互递归的类型限制,Coq的终止检查器能直接识别出参数的递减关系:

Inductive T := | foo : T | bar : nat -> T -> T.

Fixpoint evalT (t:T) : nat :=
  match t with
  | foo => 1
  | bar n x =>
      (* 把evalBar嵌套在bar分支里,作为局部函数 *)
      let fix evalBar (m:nat) : nat :=
        match m with
        | O => 0
        | S m' => evalT x + evalBar m'
        end
      in evalBar n
  end.

这个版本里,evalBar的递归参数m每次都会减1,而evalT x的参数x是bar n x的子项,结构肯定比原参数小,Coq会直接通过检查,连额外证明都不需要。

方案3:用Function命令支持跨类型互递归

如果你一定要保留互递归的结构,可以用Coq的Recdef库提供的Function命令,它支持更灵活的终止性证明,包括跨不同归纳类型的互递归:

Require Import Recdef.

Inductive T := | foo : T | bar : nat -> T -> T.

Function evalT (t:T) : nat :=
  match t with
  | foo => 1
  | bar n x => evalBar x n
  end
with evalBar (x:T) (n:nat) : nat :=
  match n with
  | O => 0
  | S n' => evalT x + evalBar x n'
  end.
Proof.
  (* 定义一个组合的良基关系:要么T类型的参数结构更小,要么nat参数更小 *)
  intros.
  apply (well_founded_compose (Wf_nat.lt_wf) (size)) with (x := (x, n)).
  - intros (x1, n1) (x2, n2) H.
    destruct H as [H1 | H2].
    + left; apply H1.
    + right; apply H2.
  - destruct t; simpl.
    + auto.
    + right; apply lt_n_S.
  - destruct n; simpl.
    + auto.
    + left; apply le_n_S; reflexivity.
Qed.

如果只是简单场景,方案2绝对是最省心的;如果必须保留互递归结构,方案1或3都能解决问题。

内容的提问来源于stack exchange,提问作者krokodil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:38:54