坚持建造一个非常简单的功能

时间:2015-01-31 16:14:48

标签: coq

我正在学习Coq。我陷入了一个非常愚蠢的问题(没有动力,真的很傻)。我想构建一个从2,+ oo]到映射x到x-3的整数集的函数。这应该很简单......在我知道的任何语言中,它很简单。但不是在Coq。首先,我写(我解释了很多细节,以便有人可以解释我在Coq的行为中不理解的东西)

Definition f : forall n : nat, n > 2 -> nat.

我得到一个子目标

  ============================
   forall n : nat, n > 2 -> nat

这意味着Coq需要从n> 2的证明到整数集的映射。精细。所以我想告诉它n = 3 + p对于某个整数p,然后返回整数p。我写道:

intros n H.

我得到了上下文/子目标

  n : nat
  H : n > 2
  ============================
   nat

然后我想我已经证明n = 3 + p对于某个整数p

cut(exists p, 3 + p = n).

我得到了上下文/子目标

  n : nat
  H : n > 2
  ============================
   (exists p : nat, 3 + p = n) -> nat

subgoal 2 (ID 6) is:
 exists p : nat, 3 + p = n

我通过

在上下文中移动假设
intro K.

我获得:

  n : nat
  H : n > 2
  K : exists p : nat, 3 + p = n
  ============================
   nat

subgoal 2 (ID 6) is:
 exists p : nat, 3 + p = n

我稍后会证明p的存在。现在我想通过精确的p完成证明。所以我首先需要做一个

destruct K as (p,K).

我收到错误消息

  

错误:对于归纳,不允许对排序集进行案例分析   定义ex。

我被困住了。

1 个答案:

答案 0 :(得分:5)

你是对的!用任何合理的编程语言编写这个函数都应该很容易,而且幸运的是,Coq也不例外。

在您的情况下,通过忽略您提供的证明参数来定义您的函数要容易得多:

Definition f (n : nat) : nat := n - 3.

然后你可能想知道“但是等一下,自然数字在减法下不会被关闭,所以这有什么意义呢?”好吧,在Coq中,对自然数的减法并不是真正的减法:它实际上是截断。如果你试图从2减去,比如说3,你会得到0作为答案:

Goal 2 - 3 = 0. reflexivity. Qed.

这在实践中意味着你总是被允许“减去”两个自然数并得到一个自然数,但为了使这个减法有意义,第一个参数需要大于第二个参数。然后我们得到以下的引理(在standard library中可用):

le_plus_minus_r : forall n m, n <= m -> n + (m - n) = m

在大多数情况下,使用部分正确的函数(例如此减法定义)就足够了。但是,如果您愿意,可以限制f的域以使其属性更加舒适。我冒昧地使用ssreflect库执行以下脚本,这使得编写此类函数变得更容易:

Require Import Ssreflect.ssreflect Ssreflect.ssrfun Ssreflect.ssrbool.
Require Import Ssreflect.ssrnat Ssreflect.eqtype.

Definition f (n : {n | 2 < n}) : nat :=
  val n - 3.

Definition finv (m : nat) : {n | 2 < n} :=
  Sub (3 + m) erefl.

Lemma fK : cancel f finv.
Proof.
move=> [n Pn] /=; apply/val_inj=> /=.
by rewrite /f /= addnC subnK.
Qed.

Lemma finvK : cancel finv f.
Proof.
by move=> n; rewrite /finv /f /= addnC addnK.
Qed.

现在,f将自然数n作为参数,大于2{x : T | P x}形式是sig类型的语法糖来自标准库,用于形成类似子集的类型)。通过限制参数类型,我们可以编写一个反函数finv,它采用任意nat并返回另一个大于2的数字。然后,我们可以证明lemmas fKfinvK,它们断言fKfinvK是彼此相反的。

关于f的定义,我们使用val,这是ssreflect的习惯用法,用于从{n | 2 < n}类型的成员中提取元素。 Sub finv上的n功能恰恰相反,使用2 < n的证明打包自然数{n | 2 < n}并返回<元素。在这里,我们主要依赖于erefl在ssreflect中表示为布尔计算的事实,因此Coq可以使用其计算规则来检查true = true2 < 3 + m的证明,也是Type的有效证明。

总而言之,您最终得到的神秘错误信息与Coq管理计算类型的规则有关,其中包含Prop中的live和生成{p | 3 + p = n}的命题类型。 Coq的规则禁止您使用命题证明来构建具有计算内容(例如自然数)的元素,除非在非常特殊的情况下。如果您愿意,您仍然可以使用exists p, 3 + p = n代替Type来完成定义 - 两者的意思相同,除了前者生活在Prop中,而后者生活在{{1} }}