我可以在不让绑定的情况下在Coq中进行“复杂的”相互递归吗?

时间:2018-10-01 21:46:44

标签: recursion coq termination mutual-recursion

请考虑以下一对相互递归的Coq数据类型,它们代表非空Forest的{​​{1}}。 Tree中的每个Branch都有一个额外的布尔值标志,我们可以使用Tree提取该标志。

isOK

现在,如果忽略此布尔值标志,我们可以编写一对映射函数,以将函数应用于Inductive Forest a : Type := Empty : Forest a | WithTree : Tree a -> Forest a -> Forest a with Tree a : Type := Branch : bool -> a -> Forest a -> Tree a. Arguments Empty {_}. Arguments WithTree {_} _ _. Arguments Branch {_} _ _ _. Definition isOK {a} (t : Tree a) : bool := match t with | Branch ok _ _ => ok end. Forest中的每个值,并且可以正常工作:

Tree

但是,假设布尔值表示某种有效性检查,这在实际代码中会更加复杂。因此,我们首先检查布尔值,并且仅在必要时才实际递归。这意味着我们具有三个相互递归函数,但是其中之一只是在进行工作。不幸的是,这行不通:

Fixpoint mapForest_always {a} (f : a -> a) (ts0 : Forest a) {struct ts0} : Forest a :=
  match ts0 with
  | Empty         => Empty
  | WithTree t ts => WithTree (mapTree_always f t) (mapForest_always f ts)
  end
with mapTree_always {a} (f : a -> a) (t : Tree a) {struct t} : Tree a :=
  match t with
  | Branch ok x ts => Branch ok (f x) (mapForest_always f ts)
  end.

问题是Fail Fixpoint mapForest_bad {a} (f : a -> a) (ts0 : Forest a) {struct ts0} : Forest a := match ts0 with | Empty => Empty | WithTree t ts => WithTree (mapTree_bad f t) (mapForest_bad f ts) end with mapTree_bad {a} (f : a -> a) (t : Tree a) {struct t} : Tree a := if isOK t then mapOKTree_bad f t else t with mapOKTree_bad {a} (f : a -> a) (t : Tree a) {struct t} : Tree a := match t with | Branch ok x ts => Branch ok (f x) (mapForest_bad f ts) end. 调用了实际上不是更小的参数的mapTree_bad

除了…mapOKTree_bad的所有操作之外,还需要进行一些预处理。此总是终止,但Coq看不到。为了说服终止检查器,我们可以改为定义mapOKTree_bad,它是相同的,但是是本地mapOKTree_good绑定;然后,终止检查器将查看let绑定,并允许我们定义letmapForest_good。如果我们想获得mapTree_good,则可以在定义了相互递归函数后使用简单的旧定义,该函数的主体与mapOKTree_good绑定相同:

let

这行得通,但是并不漂亮。有什么方法可以说服Coq的终止检查器接受Fixpoint mapForest_good {a} (f : a -> a) (ts0 : Forest a) {struct ts0} : Forest a := match ts0 with | Empty => Empty | WithTree t ts => WithTree (mapTree_good f t) (mapForest_good f ts) end with mapTree_good {a} (f : a -> a) (t : Tree a) {struct t} : Tree a := let mapOKTree_good {a} (f : a -> a) (t : Tree a) : Tree a := match t with | Branch ok x ts => Branch ok (f x) (mapForest_good f ts) end in if isOK t then mapOKTree_good f t else t. Definition mapOKTree_good {a} (f : a -> a) (t : Tree a) : Tree a := match t with | Branch ok x ts => Branch ok (f x) (mapForest_good f ts) end. 变体,还是_bad诀窍是我所拥有的最好方法?像_goodProgram Fixpoint这样对我有用的命令也是完全合理的解决方案。

1 个答案:

答案 0 :(得分:3)

非常部分的答案:我们可以在定义mapOKTree_good之前,通过由mapForest_good参数化的中间定义来重构Definition mapOKTree_good_ {a} mapForest_good (f : a -> a) (t : Tree a) : Tree a := match t with | Branch ok x ts => Branch ok (f x) (mapForest_good f ts) end. Fixpoint mapForest_good {a} (f : a -> a) (ts0 : Forest a) {struct ts0} : Forest a := match ts0 with | Empty => Empty | WithTree t ts => WithTree (mapTree_good f t) (mapForest_good f ts) end with mapTree_good {a} (f : a -> a) (t : Tree a) {struct t} : Tree a := if isOK t then mapOKTree_good_ mapForest_good f t else t. Definition mapOKTree_good {a} := @mapOKTree_good_ a mapForest_good. 的两个定义。

Events.insert({ 'start': new Date(2018, 9, 5, 7, 0, 0, 0) });