在Isabelle教程中练习练习时,我遇到了让我感到困惑的情况。为什么下面涉及前置列表的引理很容易被证明:
lemma ‹count_list xs x = n ⟹ count_list (x # xs) x = Suc n›
by simp
虽然这个涉及追加的不是吗?
lemma ‹count_list xs x = n ⟹ count_list (xs @ [x]) x = Suc n›
apply auto (* just does some minor rewriting *)
(* What should be go here? try/try0 are of no help. *)
done
我认为这与@
的实现比#
更为复杂,因为列表的内部实现(#
只是@
的另一个构造函数。涉及递归),但是什么使它更难以证明,你如何解释?
答案 0 :(得分:2)
这与@
的“实施”无关。它与count_list
的“实现”有关(但我会说'定义'而不是'实现')。
与列表上的大多数函数一样,count_list
是递归定义的(使用primrec
命令),它会生成添加到simpset的规则count_list.simps
(规则集,简化器用于自动重写。
如果您输入thm count_list.simps
,您可能会得到以下内容:
count_list [] ?y = 0
count_list (?x # ?xs) ?y =
(if ?x = ?y then count_list ?xs ?y + 1 else count_list ?xs ?y)
由于它们位于simpset中,因此作为auto
的一部分调用的简化器将始终将左侧的任何实例重写到右侧。
因此,您给出的第一个定理可以通过使用这两个规则中的第二个重写的一个步骤和一些更简化来证明。在这方面,该定理的等效且更有用的公式为count_list (x # xs) x = Suc (count_list xs x)
。
同样,我非常建议将你的第二个引理重新定义为count_list (xs @ [x]) x = Suc (count_list xs x)
。
现在为什么auto
无法解决这个问题?首先,对于这种目标,auto
这个目标的唯一相关部分是简化器,所以你也可以在这里编写simp_all
。而无法解决目标的原因在于它没有像count_list (… @ …)
那样的简化规则。你必须自己证明这一点。
当您想要证明递归定义函数的属性时,通常的方法是进行反映递归定义的归纳:
lemma ‹count_list (xs @ [x]) x = Suc (count_list xs x)›
apply (induction xs)
apply simp_all
done
或更紧凑:
lemma ‹count_list (xs @ [x]) x = Suc (count_list xs x)›
by (induction xs) simp_all
但我实际上会推荐以下更一般的引理:
lemma ‹count_list (xs @ ys) x = count_list xs x + count_list ys x›
by (induction xs) simp_all
事实上,给这个事实命名是合理的,并将它添加到simp集中,因为它是一个很好的重写规则:
lemma count_list_append [simp]:
‹count_list (xs @ ys) x = count_list xs x + count_list ys x›
by (induction xs) simp_all
如果你想知道为什么我在这里xs
而不是ys
上进行归纳:因为count_list
模式的递归定义匹配第一个列表元素和剩余列表,必须在第一个列表上进行归纳(即xs
),以便归纳步骤讨论(x # xs) @ ys
,简化为x # (xs @ ys)
,然后可以应用归纳假设。
请注意simp_all
(或auto
)足以证明上述证据中的归纳目标;一旦你告诉伊莎贝尔做感应和什么样的感应,其余的证明只是简单的重写。