我无法证明终止以下功能:
Fixpoint norm_union u v : regex :=
match u, v with
| Empty , v => v
| u , Empty => u
| Union u v, w => norm_union u (norm_union v w)
| u , Union v w => if eq_regex_dec u v
then Union v w
else if le_regex u v
then Union u (Union v w)
else Union v (norm_union u w)
| u , v => if eq_regex_dec u v
then u
else if le_regex u v
then Union u v
else Union v u
end.
其中regex
是正则表达式的类型,le_regex
实现正则表达式的总排序。源是this文档的第5页。该函数作为正则表达式的规范化函数的一部分出现(在Isabelle / HOL中形式化)。 le_regex
功能改编自同一篇论文。我使用ascii
来避免通过可判定的总排序参数化regex
(并且想要提取程序)。
Inductive regex : Set :=
| Empty : regex
| Epsilon : regex
| Symbol : ascii -> regex
| Union : regex -> regex -> regex
| Concat : regex -> regex -> regex
| Star : regex -> regex.
Lemma eq_regex_dec : forall u v : regex, {u = v} + {u <> v}.
Proof. decide equality; apply ascii_dec. Defined.
Fixpoint le_regex u v : bool :=
match u, v with
| Empty , _ => true
| _ , Empty => false
| Epsilon , _ => true
| _ , Epsilon => false
| Symbol a , Symbol b => nat_of_ascii a <=? nat_of_ascii b
| Symbol _ , _ => true
| _ , Symbol _ => false
| Star u , Star v => le_regex u v
| Star u , _ => true
| _ , Star v => false
| Union u1 u2 , Union v1 v2 => if eq_regex_dec u1 v1
then le_regex u2 v2
else le_regex u1 v1
| Union _ _ , _ => true
| _ , Union _ _ => false
| Concat u1 u2, Concat v1 v2 => if eq_regex_dec u1 v1
then le_regex u2 v2
else le_regex u1 v1
end.
我认为正确的方法是定义递减度量并使用Program Fixpoint
来证明终止。但是,我在提出正确的测量方法时遇到了麻烦(基于操作员数量的尝试未成功)。我已经尝试将工作分解为单独的函数,但遇到了类似的问题。任何帮助将不胜感激,或提示指向正确的方向。
答案 0 :(得分:3)
您的代码比通常使用measure函数处理的代码更复杂,因为您在以下行中有一个嵌套的递归调用:
Union u v, w => norm_union u (norm_union v w) (* line 5 *)
我建议您不要在regex
类型中返回值,而应在类型{r : regex | size r < combined_size u v}
中返回size
和combined_size
的合适概念。
在对您的问题进行了几个小时的研究后,结果还证明您的递归依赖于参数的词法排序。 norm_union v w
可能会返回Union v w
,因此您需要参数对(u, Union v w)
小于(Union u v, w)
。
因此,如果您真的想要使用度量,则需要左侧的权重大于右侧的权重,并且您需要对Union
的分量进行度量小于整体的衡量标准。
由于词汇排序性质,我选择不使用度量而是使用有根据的命令。另外,我不太了解Program Fixpoint
,所以我使用其他工具开发了解决问题的方法。我想出的解决方案可以看出here on github。至少这表明需要证明的所有减少条件。
答案 1 :(得分:2)
经过一天的工作后,我现在对这个问题有了更完整的答案。它仍然可以在this link看到。这个解决方案值得一些评论。
首先,我使用一个名为Fix
的函数构造函数(长名称为Coq.Init.Wf.Fix
。这是一个更高阶函数,可用于通过有根据的递归来定义函数。我需要一个对此有充分理由的命令,这个命令被称为order
。在21世纪初,他们仍然在Program Fixpoint
命令的基础上对有根据的命令进行了深入的研究。
其次,您编写的代码同时对两个类型为regex
的值进行案例分析,因此这会导致36个案例(稍微少一些,因为当第一个参数为第二个参数时,没有案例分析Empty
)。您没有在代码中看到36个案例,因为几个构造函数被同一规则覆盖,其中模式只是一个变量。为了避免这种情况的增加,我设计了一种特定的归纳类型
案例分析。我调用了这种特定类型arT
。然后我定义了一个函数ar
,它将regex
类型的任何元素映射到arT
的相应元素。类型arT
有三个构造函数而不是六个,因此模式加工表达式将包含更少的代码,并且证明将更简洁。
然后我继续使用norm_union
定义Fix
。像往常一样在Coq(和大多数定理证明,包括Isabelle),语言
递归定义确保递归函数始终终止。在这种情况下,这是通过强制递归调用仅发生在比函数输入更小的参数上来完成的。在这种情况下,这是通过一个函数描述递归函数的主体来完成的,该函数将初始输入作为第一个参数,并将第二个参数作为将用于表示递归调用的函数。此函数的名称为norm_union_F
,其类型如下:
forall p : regex * regex,
forall g : (forall p', order p' p ->
{r : regex | size_regex r <= size_2regex p'}),
{r : regex | size_regex r <= size_2regex p}
在此类型描述中,用于表示递归调用的函数名称为g
,我们发现g
的类型强制它只能用于{{1}对对于名为regex
的订单,小于初始参数p
的术语。在这种类型的描述中,我们也看到我选择表示返回的递归调用类型不是order
而是regex
。这是因为我们必须处理嵌套递归,其中递归调用的输出将用作其他递归调用的输入。 这是此答案的主要技巧。
然后我们有{r : regex | size_regex r <= size_2regex p'}
函数的主体:
norm_union_F
在此代码中,所有输出值都在Definition norm_union_F : forall p : regex * regex,
forall g : (forall p', order p' p ->
{r : regex | size_regex r <= size_2regex p'}),
{r : regex | size_regex r <= size_2regex p} :=
fun p norm_union =>
match ar (fst p) with
arE _ eq1 => exist _ (snd p) (th1 p)
| arU _ u v eq1 =>
match ar (snd p) with
arE _ eq2 => exist _ (Union u v) (th2' _ _ _ eq1)
| _ => exist _ (proj1_sig
(norm_union (u,
proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
_)))
(th5' _ _ _ eq1
(proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(proj1_sig
(norm_union
(u, proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p) (th3' _ _ _ eq1))))))
(proj2_sig
(norm_union
(u, proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))))))
end
| arO _ d1 d2 =>
match ar (snd p) with
arE _ eq2 => exist _ (fst p) (th11' _)
| arU _ v w eq2 =>
if eq_regex_dec (fst p) v then
exist _ (Union v w) (th7' _ _ _ eq2)
else if le_regex (fst p) v then
exist _ (Union (fst p) (Union v w)) (th8' _ _ _ eq2)
else exist _ (Union v (proj1_sig (norm_union (fst p, w)
(th9' _ _ _ eq2))))
(th10' _ _ _ eq2
(proj1_sig (norm_union (fst p, w)
(th9' _ _ _ eq2)))
(proj2_sig (norm_union (fst p, w)
(th9' _ _ _ eq2))))
| arO _ d1 d2 =>
if eq_regex_dec (fst p) (snd p) then
exist _ (fst p) (th11' _)
else if le_regex (fst p) (snd p) then
exist _ (Union (fst p) (snd p)) (th12' _)
else exist _ (Union (snd p) (fst p)) (th13' _)
end
end.
上下文中:我们不仅生成输出值,而且还显示此值的大小小于输入对的组合大小。值。此外,所有递归调用都在exist _
上下文中,因此我们在构造输出值时忘记了大小信息。但是,所有递归调用,这里通过对名为proj1_sig
的函数的调用来表示也有证据表明递归调用的输入确实小于初始输入。所有证明都在the complete development。
可能会使用norm_union
这样的策略来定义refine
,邀请您进行探索。
然后我们定义真正的递归函数norm_union_F
norm_union_1
请注意Definition norm_union_1 : forall p : regex*regex,
{x | size_regex x <= size_2regex p} :=
Fix well_founded_order (fun p => {x | size_regex x <= size_2regex p})
norm_union_F.
的输出类型为norm_union_1
。这不是您要求的类型。所以我们定义一个新函数,它实际上是你想要的函数,只需忘记输出的大小小于输入的逻辑信息。
{x | size_regex x <= size_2regex p}
您可能仍然怀疑这是正确的功能,您要求的功能。为了说服自己,我们将证明一个引理,它完全表达了你在定义中所说的内容。
我们首先证明Definition norm_union u v : regex := proj1_sig (norm_union_1 (u, v)).
的相应引理。这取决于与norm_union_1
函数关联的定理,名称Fix
。需要做的证明是相当常规的(它总是可以自动完成,但我从来没有开始为此开发自动工具)。
然后我们以最有趣的引理结束,Fix_eq
。以下是声明:
norm_union
请注意,此等式的右侧正是您在初始问题中给出的代码(我只是复制粘贴它)。这个最终定理的证明也是相当系统的。
现在,我努力完全遵循您的请求,但事实上我发现使用三个递归函数可以实现相同功能的简单实现。第一个将Lemma norm_union_eqn u v :
norm_union u v =
match u, v with
| Empty , v => v
| u , Empty => u
| Union u v, w => norm_union u (norm_union v w)
| u , Union v w => if eq_regex_dec u v
then Union v w
else if le_regex u v
then Union u (Union v w)
else Union v (norm_union u w)
| u , v => if eq_regex_dec u v
then u
else if le_regex u v
then Union u v
else Union v u
end.
的二叉树展平,然后使其看起来像列表,另外两个按照Union
的顺序对这些联合进行排序,同时在发现它们时立即删除重复项。这样的实现可以解决嵌套递归的需要。
如果您仍然希望坚持嵌套递归并需要参考此处描述的技术,那么它首先发布在Balaa和Bertot在TPHOLs2000上的一篇论文中。这篇论文难以阅读,因为它是在Coq使用不同语法的时候编写的。