“软件基础”中有一个练习,我一直在尝试正确解决一段时间,但实际上我在尝试写下所要求的功能方面遇到了困难。这是练习的相关部分
考虑使用二进制而不是一元系统的自然数的不同、更有效的表示。也就是说,与其说每个自然数要么是零要么是自然数的后继,我们可以说每个二进制数要么是
- 零,
- 二进制数的两倍,或
- 一个二进制数的两倍多。
(a) 首先,写出对应于二进制数描述的类型 bin 的归纳定义。
幼稚的定义不太适用,因为您最终能够构建将 1 加到已经加了 1 的数字或无缘无故地将 0 乘以 2 的术语。为了避免那些我想我会在构造函数中编码某种状态转换以避免这些,但这有点棘手所以这是我能想到的最好的
Inductive tag : Type := z | nz | m. (* zero | nonzero | multiply by 2 *)
Inductive bin_nat : tag -> Type :=
(* zero *)
| Zero : bin_nat z
(* nonzero *)
| One : bin_nat nz
(* multiply by 2 -> nonzero *)
| PlusOne : bin_nat m -> bin_nat nz
(* nonzero | multiply by 2 -> multiply by 2 *)
| Multiply : forall {t : tag}, (t = m \/ t = nz) -> bin_nat t -> bin_nat m.
通过上述表示,我避免了没有意义的术语问题,但现在每当我乘以 2 时我都必须携带证明。我实际上不知道如何在递归函数中使用这些东西。
我知道如何构建证明和术语,它们看起来像这样
(* nonzero *)
Definition binr (t : tag) := or_intror (t = m) (eq_refl nz).
(* multiply by 2 *)
Definition binl (t : tag) := or_introl (t = nz) (eq_refl tag m).
(* some terms *)
Check Zero.
Check One.
Check (Multiply (binr _) One).
Check (Multiply (binl _) (Multiply (binr _) One)).
Check PlusOne (Multiply (binl _) (Multiply (binr _) One)).
我还可以写下我想要对应于函数的定理的“证明”,但我不知道如何将其实际转换为函数。这是转换函数的证明
Definition binary_to_nat : forall t : tag, bin_nat t -> nat.
Proof.
intros.
einduction H as [ | | b | t proof b ].
{ exact 0. } (* Zero *)
{ exact 1. } (* One *)
{ exact (S (IHb b)). } (* PlusOne *)
{ (* Multiply *)
edestruct t.
cut False.
intros F.
case F.
case proof.
intros F.
inversion F.
intros F.
inversion F.
exact (2 * (IHb b)).
exact (2 * (IHb b)).
}
Defined.
我知道这个术语是我想要的函数,因为我可以验证当我用它计算时我得到了正确的答案
Section Examples.
Example a : binary_to_nat z Zero = 0.
Proof.
lazy.
trivial.
Qed.
Example b : binary_to_nat nz One = 1.
Proof.
lazy.
trivial.
Qed.
Example c : binary_to_nat m ((Multiply (binl _) (Multiply (binr _) One))) = 4.
Proof.
lazy.
trivial.
Qed.
End Examples.
所以最后的问题是,有没有一种简单的方法可以以简单的方式将上述证明项转换为实际函数,还是我必须尝试对证明项进行逆向工程?