嵌套递归和“Program Fixpoint”或“Function”
Nested recursion and `Program Fixpoint` or `Function`
我想在 Coq 中使用 Program Fixpoint
或 Function
定义以下函数:
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Coq.Program.Wf.
Require Import Recdef.
Inductive Tree := Node : nat -> list Tree -> Tree.
Fixpoint height (t : Tree) : nat :=
match t with
| Node x ts => S (fold_right Nat.max 0 (map height ts))
end.
Program Fixpoint mapTree (f : nat -> nat) (t : Tree) {measure (height t)} : Tree :=
match t with
Node x ts => Node (f x) (map (fun t => mapTree f t) ts)
end.
Next Obligation.
不幸的是,此时我有举证义务height t < height (Node x ts)
,但不知道t
是ts
的成员。
与 Function
而非 Program Fixpoint
类似,只是 Function
检测到问题并中止定义:
Error:
the term fun t : Tree => mapTree f t can not contain a recursive call to mapTree
我希望得到 In t ts → height t < height (Node x ts)
的证明义务。
有没有一种不涉及重构函数定义的方法? (例如,我知道需要在此处内联 map
定义的变通方法——我想避免这些。)
伊莎贝尔
为了证明这种期望,让我展示一下当我在 Isabelle 中使用 function
命令时会发生什么,该命令(据我所知)与 Coq 的 Function
命令相关:
theory Tree imports Main begin
datatype Tree = Node nat "Tree list"
fun height where
"height (Node _ ts) = Suc (foldr max (map height ts) 0)"
function mapTree where
"mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)"
by pat_completeness auto
termination
proof (relation "measure (λ(f,t). height t)")
show "wf (measure (λ(f, t). height t))" by auto
next
fix f :: "nat ⇒ nat" and x :: nat and ts :: "Tree list" and t
assume "t ∈ set ts"
thus "((f, t), (f, Node x ts)) ∈ measure (λ(f, t). height t)"
by (induction ts) auto
qed
在终止证明中,我得到假设t ∈ set ts
。
注意 Isabelle 在这里不需要手动终止证明,下面的定义就可以了:
fun mapTree where
"mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)"
这是有效的,因为 map
函数有一个形式为
的“同余引理”
xs = ys ⟹ (⋀x. x ∈ set ys ⟹ f x = g x) ⟹ map f xs = map g ys
那个function
命令用来找出终止证明只需要考虑t ∈ set ts
..
如果这样的引理不可用,例如因为我定义
definition "map' = map"
并在 mapTree
中使用它,我得到了与 Coq 中相同的无法证明的证明义务。我可以通过为 map'
声明一个同余引理来使其再次工作,例如使用
declare map_cong[folded map'_def,fundef_cong]
在这种情况下,您实际上不需要具有充分普遍性的有根据的递归:
Require Import Coq.Lists.List.
Set Implicit Arguments.
Inductive tree := Node : nat -> list tree -> tree.
Fixpoint map_tree (f : nat -> nat) (t : tree) : tree :=
match t with
| Node x ts => Node (f x) (map (fun t => map_tree f t) ts)
end.
Coq 能够自行判断对 map_tree
的递归调用是在严格的子项上执行的。然而,要证明关于这个函数的任何事情都很困难,因为为 tree
生成的归纳原理没有用:
tree_ind :
forall P : tree -> Prop,
(forall (n : nat) (l : list tree), P (Node n l)) ->
forall t : tree, P t
这基本上与您之前描述的问题相同。幸运的是,我们可以通过用证明项证明我们自己的归纳原理来解决这个问题。
Require Import Coq.Lists.List.
Import ListNotations.
Unset Elimination Schemes.
Inductive tree := Node : nat -> list tree -> tree.
Set Elimination Schemes.
Fixpoint tree_ind
(P : tree -> Prop)
(IH : forall (n : nat) (ts : list tree),
fold_right (fun t => and (P t)) True ts ->
P (Node n ts))
(t : tree) : P t :=
match t with
| Node n ts =>
let fix loop ts :=
match ts return fold_right (fun t' => and (P t')) True ts with
| [] => I
| t' :: ts' => conj (tree_ind P IH t') (loop ts')
end in
IH n ts (loop ts)
end.
Fixpoint map_tree (f : nat -> nat) (t : tree) : tree :=
match t with
| Node x ts => Node (f x) (map (fun t => map_tree f t) ts)
end.
Unset Elimination Schemes
命令阻止 Coq 为 tree
生成其默认的(且无用的)归纳原理。 fold_right
在归纳假设上的出现简单地表示谓词 P
对出现在 ts
.
中的每棵树 t'
成立
这里有一个命题,你可以用这个归纳原理来证明:
Lemma map_tree_comp f g t :
map_tree f (map_tree g t) = map_tree (fun n => f (g n)) t.
Proof.
induction t as [n ts IH]; simpl; f_equal.
induction ts as [|t' ts' IHts]; try easy.
simpl in *.
destruct IH as [IHt' IHts'].
specialize (IHts IHts').
now rewrite IHt', <- IHts.
Qed.
一般来说,最好避免这个问题。但是如果真的想获得Isabelle给你的举证义务,这里有一个办法:
在 Isabelle 中,我们可以给出一个外部引理,表明 map
仅将其参数应用于给定列表的成员。在 Coq 中,我们不能在外部引理中这样做,但我们可以在类型中这样做。所以不是普通类型的地图
forall A B, (A -> B) -> list A -> list B
我们希望类型表示“f
仅应用于列表的元素:
forall A B (xs : list A), (forall x : A, In x xs -> B) -> list B
(需要重新排序参数,这样f
的类型才能提到xs
)。
编写这个函数并不简单,我发现使用证明脚本更容易:
Definition map {A B} (xs : list A) (f : forall (x:A), In x xs -> B) : list B.
Proof.
induction xs.
* exact [].
* refine (f a _ :: IHxs _).
- left. reflexivity.
- intros. eapply f. right. eassumption.
Defined.
但你也可以“手写”:
Fixpoint map {A B} (xs : list A) : forall (f : forall (x:A), In x xs -> B), list B :=
match xs with
| [] => fun _ => []
| x :: xs => fun f => f x (or_introl eq_refl) :: map xs (fun y h => f y (or_intror h))
end.
无论哪种情况,结果都很好:我可以在 mapTree
中使用此函数,即
Program Fixpoint mapTree (f : nat -> nat) (t : Tree) {measure (height t)} : Tree :=
match t with
Node x ts => Node (f x) (map ts (fun t _ => mapTree f t))
end.
Next Obligation.
而且我不需要对 f
的新参数做任何事情,但它会在终止证明义务中显示为 In t ts → height t < height (Node x ts)
,如我所愿。所以我可以证明并定义 mapTree
:
simpl.
apply Lt.le_lt_n_Sm.
induction ts; inversion_clear H.
- subst. apply PeanoNat.Nat.le_max_l.
- rewrite IHts by assumption.
apply PeanoNat.Nat.le_max_r.
Qed.
不幸的是,它仅适用于 Program Fixpoint
,不适用于 Function
。
您现在可以使用方程式执行此操作并自动获得正确的消元原理,使用 structural nested recursion or well-founded recursion
我想在 Coq 中使用 Program Fixpoint
或 Function
定义以下函数:
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Coq.Program.Wf.
Require Import Recdef.
Inductive Tree := Node : nat -> list Tree -> Tree.
Fixpoint height (t : Tree) : nat :=
match t with
| Node x ts => S (fold_right Nat.max 0 (map height ts))
end.
Program Fixpoint mapTree (f : nat -> nat) (t : Tree) {measure (height t)} : Tree :=
match t with
Node x ts => Node (f x) (map (fun t => mapTree f t) ts)
end.
Next Obligation.
不幸的是,此时我有举证义务height t < height (Node x ts)
,但不知道t
是ts
的成员。
与 Function
而非 Program Fixpoint
类似,只是 Function
检测到问题并中止定义:
Error: the term fun t : Tree => mapTree f t can not contain a recursive call to mapTree
我希望得到 In t ts → height t < height (Node x ts)
的证明义务。
有没有一种不涉及重构函数定义的方法? (例如,我知道需要在此处内联 map
定义的变通方法——我想避免这些。)
伊莎贝尔
为了证明这种期望,让我展示一下当我在 Isabelle 中使用 function
命令时会发生什么,该命令(据我所知)与 Coq 的 Function
命令相关:
theory Tree imports Main begin
datatype Tree = Node nat "Tree list"
fun height where
"height (Node _ ts) = Suc (foldr max (map height ts) 0)"
function mapTree where
"mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)"
by pat_completeness auto
termination
proof (relation "measure (λ(f,t). height t)")
show "wf (measure (λ(f, t). height t))" by auto
next
fix f :: "nat ⇒ nat" and x :: nat and ts :: "Tree list" and t
assume "t ∈ set ts"
thus "((f, t), (f, Node x ts)) ∈ measure (λ(f, t). height t)"
by (induction ts) auto
qed
在终止证明中,我得到假设t ∈ set ts
。
注意 Isabelle 在这里不需要手动终止证明,下面的定义就可以了:
fun mapTree where
"mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)"
这是有效的,因为 map
函数有一个形式为
xs = ys ⟹ (⋀x. x ∈ set ys ⟹ f x = g x) ⟹ map f xs = map g ys
那个function
命令用来找出终止证明只需要考虑t ∈ set ts
..
如果这样的引理不可用,例如因为我定义
definition "map' = map"
并在 mapTree
中使用它,我得到了与 Coq 中相同的无法证明的证明义务。我可以通过为 map'
声明一个同余引理来使其再次工作,例如使用
declare map_cong[folded map'_def,fundef_cong]
在这种情况下,您实际上不需要具有充分普遍性的有根据的递归:
Require Import Coq.Lists.List.
Set Implicit Arguments.
Inductive tree := Node : nat -> list tree -> tree.
Fixpoint map_tree (f : nat -> nat) (t : tree) : tree :=
match t with
| Node x ts => Node (f x) (map (fun t => map_tree f t) ts)
end.
Coq 能够自行判断对 map_tree
的递归调用是在严格的子项上执行的。然而,要证明关于这个函数的任何事情都很困难,因为为 tree
生成的归纳原理没有用:
tree_ind :
forall P : tree -> Prop,
(forall (n : nat) (l : list tree), P (Node n l)) ->
forall t : tree, P t
这基本上与您之前描述的问题相同。幸运的是,我们可以通过用证明项证明我们自己的归纳原理来解决这个问题。
Require Import Coq.Lists.List.
Import ListNotations.
Unset Elimination Schemes.
Inductive tree := Node : nat -> list tree -> tree.
Set Elimination Schemes.
Fixpoint tree_ind
(P : tree -> Prop)
(IH : forall (n : nat) (ts : list tree),
fold_right (fun t => and (P t)) True ts ->
P (Node n ts))
(t : tree) : P t :=
match t with
| Node n ts =>
let fix loop ts :=
match ts return fold_right (fun t' => and (P t')) True ts with
| [] => I
| t' :: ts' => conj (tree_ind P IH t') (loop ts')
end in
IH n ts (loop ts)
end.
Fixpoint map_tree (f : nat -> nat) (t : tree) : tree :=
match t with
| Node x ts => Node (f x) (map (fun t => map_tree f t) ts)
end.
Unset Elimination Schemes
命令阻止 Coq 为 tree
生成其默认的(且无用的)归纳原理。 fold_right
在归纳假设上的出现简单地表示谓词 P
对出现在 ts
.
t'
成立
这里有一个命题,你可以用这个归纳原理来证明:
Lemma map_tree_comp f g t :
map_tree f (map_tree g t) = map_tree (fun n => f (g n)) t.
Proof.
induction t as [n ts IH]; simpl; f_equal.
induction ts as [|t' ts' IHts]; try easy.
simpl in *.
destruct IH as [IHt' IHts'].
specialize (IHts IHts').
now rewrite IHt', <- IHts.
Qed.
一般来说,最好避免这个问题。但是如果真的想获得Isabelle给你的举证义务,这里有一个办法:
在 Isabelle 中,我们可以给出一个外部引理,表明 map
仅将其参数应用于给定列表的成员。在 Coq 中,我们不能在外部引理中这样做,但我们可以在类型中这样做。所以不是普通类型的地图
forall A B, (A -> B) -> list A -> list B
我们希望类型表示“f
仅应用于列表的元素:
forall A B (xs : list A), (forall x : A, In x xs -> B) -> list B
(需要重新排序参数,这样f
的类型才能提到xs
)。
编写这个函数并不简单,我发现使用证明脚本更容易:
Definition map {A B} (xs : list A) (f : forall (x:A), In x xs -> B) : list B.
Proof.
induction xs.
* exact [].
* refine (f a _ :: IHxs _).
- left. reflexivity.
- intros. eapply f. right. eassumption.
Defined.
但你也可以“手写”:
Fixpoint map {A B} (xs : list A) : forall (f : forall (x:A), In x xs -> B), list B :=
match xs with
| [] => fun _ => []
| x :: xs => fun f => f x (or_introl eq_refl) :: map xs (fun y h => f y (or_intror h))
end.
无论哪种情况,结果都很好:我可以在 mapTree
中使用此函数,即
Program Fixpoint mapTree (f : nat -> nat) (t : Tree) {measure (height t)} : Tree :=
match t with
Node x ts => Node (f x) (map ts (fun t _ => mapTree f t))
end.
Next Obligation.
而且我不需要对 f
的新参数做任何事情,但它会在终止证明义务中显示为 In t ts → height t < height (Node x ts)
,如我所愿。所以我可以证明并定义 mapTree
:
simpl.
apply Lt.le_lt_n_Sm.
induction ts; inversion_clear H.
- subst. apply PeanoNat.Nat.le_max_l.
- rewrite IHts by assumption.
apply PeanoNat.Nat.le_max_r.
Qed.
不幸的是,它仅适用于 Program Fixpoint
,不适用于 Function
。
您现在可以使用方程式执行此操作并自动获得正确的消元原理,使用 structural nested recursion or well-founded recursion