如何证明递归函数有一定的价值

How to prove that a recursive function has some value

这是一个简单的函数和一个引理:

fun count_from where
  "count_from y 0 = []"
| "count_from y (Suc x) = y # count_from (Suc y) x"

lemma "count_from 3 5 = [3,4,5,6,7]"

这只是一个例子。真正的功能比较复杂

你能建议如何证明这样的引理吗?

我使用尾递归重新定义了函数并证明了引理如下:

fun count_from2 where
  "count_from2 y 0 ys = ys"
| "count_from2 y (Suc x) ys = count_from2 (Suc y) x (ys @ [y])"

lemma "count_from2 3 5 [] = xs ⟹ xs = [3,4,5,6,7]"
  apply (erule count_from2.elims)
  apply simp
  apply (drule_tac s="xs" in sym)
  apply (erule count_from2.elims)
  apply simp
  apply (drule_tac s="xs" in sym)
  apply (erule count_from2.elims)
  apply simp
  apply (drule_tac s="xs" in sym)
  apply (erule count_from2.elims)
  by auto

当然这不是一个合适的解决方案。

我有几个问题:

  1. 是否更喜欢使用尾递归来定义函数?它通常会简化定理证明吗?
  2. 为什么不能应用函数简化规则(count_from.simpscount_from2.simps)?
  3. 我是否应该定义一个引入规则来证明第一个引理?
  4. 是否可以应用函数归纳规则来证明这样的引理?

您的问题最好表述为“我如何评估递归定义的函数并将该评估作为定理?”

答案是通常简化器应该在评估方面做得很好。这里的问题是像 1、2、3 这样的数字使用自然数的二进制表示,而函数是由 0Suc 上的模式匹配定义的。这就是您的 simps 无法应用的原因:它们仅匹配 count_from ?y 0count_from ?y (Suc ?x) 形式的条款,而 count_from 3 5 两者都不是。

你可以做的是使用定理集合eval_nat_numeral,它只是将数字如 1、2、3 重写为后继符号:

lemma "count_from 3 5 = [3,4,5,6,7]"
  by (simp add: eval_nat_numeral)

另一种可能性是 code_simpeval 证明方法,它们试图通过评估并检查你是否得到 True 来证明某种意义上“可执行”的陈述。他们在这里都工作得很好:

lemma "count_from 3 5 = [3,4,5,6,7]"
  by code_simp

两者之间的区别在于 code_simp 使用简化器,它为您提供了通过 Isabelle 内核的“适当”证明(但对于更大的示例,这可能非常慢),而 eval 使用代码生成器作为受信任的 oracle,因此 可能 不太值得信赖(尽管我在实践中从未见过问题)但速度更快。

至于你的其他问题:不,我不明白归纳法在这里是如何应用的。我不知道定义引入规则是什么意思(它们看起来像什么?)。尾递归在证明事物方面并没有真正的优势——事实上,如果你“人为地”使函数定义成为尾递归的,就像你为 count_from2 所做的那样,你实际上会使事情变得稍微困难​​一些,因为任何属性你想要证明然后需要额外的概括,然后才能通过归纳法证明它们。一个典型的例子是正常 vs 尾递归列表反转(我想你可以在“编程和证明”教程中找到它)。

另请注意,与您的 count_from 非常相似的函数已存在于库中:它称为 upt :: nat ⇒ nat ⇒ nat list 并具有 [a..<b] 形式的自定义语法。它以升序为您提供 ab-1 之间的自然数列表。最好查看“主要内容”文档以了解内容。