Coq 无法在 Z 上计算有根据的函数,但它可以在 nat 上运行

2024-01-04

我正在(为我自己)写一篇关于如何在 Coq 中进行有根据的递归的解释。 (参见 Coq'Art 书,第 15.2 章)。首先我做了一个基于的示例函数nat效果很好,但后来我又做了一次Z,当我使用Compute来评估它,它并没有一直降低到Z价值。为什么?

这是我的示例(我将文本放在注释中,以便可以将整个内容复制粘贴到编辑器中):


(* 有根据的递归测试 *)

(* TL;DR: 为了进行有根据的递归, 首先创建“功能”,然后 使用创建递归函数 Acc_iter,可访问关系的迭代器 *)

(* 作为示例,计算从 1 到 n 的级数之和, 像这样的草图:

修复 f n := (如果 n = 0 则 0 否则 n + f (n-1))

现在,让我们not对 n 使用结构递归。

相反,我们对 n 使用有根据的递归, 使用小于 ('lt') 的关系是 有根据的。函数 f 终止是因为 递归调用是在结构上进行的 较小的项(在递减的 Acc 链中)。 *)

(* 首先我们为 nat 做这件事 *)

Require Import Arith.Arith.
Require Import Program.Utils. (* for 'dec' *)
Require Import Wellfounded.

(* 从证明关系是有充分根据的, 我们可以得到一个特定元素的证明 在其域内是可访问的。

这里的Check命令不是必须的,只是为了 文档,亲爱的读者。 *)

Check well_founded : forall A : Type, (A -> A -> Prop) -> Prop.
Check lt_wf : well_founded lt.
Check (lt_wf 4711 : Acc lt 4711).

(* 首先为 f 定义一个“函数”F。它是一个函数 将“递归调用”的函数 F_rec 作为参数。 因为我们需要知道第二个分支中的 n 0 我们使用 'dec' 将布尔 if 条件转换为 总和。我们将有关它的信息发送到分支机构。

我们把大部分内容写得精炼,留下一些漏洞 稍后再补充战术。 *)

Definition F (n:nat) (F_rec : (forall y : nat, y < n -> nat)): nat.
  refine ( if dec (n =? 0) then 0 else n + (F_rec (n-1) _ ) ).

  (* now we need to show that n-1 < n, which is true for nat if n<>0 *)
  destruct n; now auto with *.
Defined.

(* 迭代器可以使用函数来调用 f 根据需要多次。

旁注:我们可以创建一个迭代器来获取最大值 递归深度 d 作为 nat 参数,并在 d 上递归,但是 那么必须提供 d,以及一个“默认值” 如果 d 达到零并且必须终止则返回 早期的。

有根据的递归的巧妙之处在于 迭代器可以在有根据的证明上递归 并且不需要任何其他结构或默认值 以保证它会终止。 *)

(* Acc_iter 的类型非常多 *)

Check Acc_iter :
      forall (A : Type) (R : A -> A -> Prop) (P : A -> Type),
       (forall x : A, (forall y : A, R y x -> P y) -> P x) -> forall x : A, Acc R x -> P x.

(* P 存在是因为返回类型可能取决于参数,
但在我们的例子中,f:nat->nat,并且 R = lt,所以我们有 *)

Check Acc_iter (R:=lt) (fun _:nat=>nat) :
  (forall n : nat, (forall y : nat, y < n -> nat) -> nat) ->
   forall n : nat, Acc lt n -> nat.

(* 这里第一个参数是迭代器采用的函数, 第二个参数 n 是 f 的输入,第三个参数是 n 可访问的证明。 迭代器返回应用于 n 的 f 值。

Acc_iter 的一些参数是隐式的,有些是可以推断的。 因此我们可以简单地定义 f 如下: *)

Definition f n := Acc_iter _ F (lt_wf n).

(*它就像一个魅力*)

Compute (f 50). (* This prints 1275 *)
Check eq_refl : f 50 = 1275.

(* 现在让我们为 Z 做这件事。这里我们不能使用 lt, 或 lt_wf 因为它们用于 nat。对于Z我们 可以使用 Zle 和 (Zwf c) 来获取下限。 它需要一个下界,在该下界下我们知道函数 将始终终止以保证终止。 这里我们使用 (Zwf 0) 来表示我们的函数将 总是在 0 或以下终止。我们还必须 将 if 语句更改为“if n

Require Import ZArith.
Require Import Zwf.

Open Scope Z.

(* 现在我们根据泛函 G 定义函数 g *)

Definition G (n:Z) (G_rec :  (forall y : Z, Zwf 0 y n -> Z)) : Z.
  refine (if dec (n<?0) then 0 else n + (G_rec (n-1) _ )).

  (* now we need to show that n-1 < n *)
  now split; [ apply Z.ltb_ge | apply Z.lt_sub_pos].
Defined.

Definition g n := Acc_iter _ G (Zwf_well_founded 0 n).

(* 但现在我们无法计算!*)

Compute (g 1).

(* 我们刚刚得到一个以

     = (fix
        Ffix (x : Z)
             (x0 : Acc
                     (fun x0 x1 : Z =>
                      (match x1 with
                       | 0 => Eq
                       | Z.pos _ => Lt
                       | Z.neg _ => Gt
                       end = Gt -> False) /\
                      match x0 with
                      | 0 => match x1 with
                             | 0 => Eq
                             | Z.pos _ => Lt
                             | Z.neg _ => Gt
                             end
                      | Z.pos x2 =>

    ...

 end) 1 (Zwf_well_founded 0 1)
     : (fun _ : Z => Z) 1
   ) 

评论:我注意到了Zwf_well_founded定义为Opaque在图书馆,所以我试着做到Transparent通过复制证明并结束引理Defined.代替Qed.但这没有帮助...

补充观察:

如果我定义f' for nat with Fixpoint相反,并递归 可访问性证明,并结束于Defined.然后它会计算。但如果我以Qed.它并没有减少。这有关系吗?我认为定义存在透明度问题G or g某处...或者我完全错了?

Fixpoint f' (n:nat) (H: Acc lt n) : nat.
  refine (if dec (n<=?0) then 0 else n + (f' (n-1) (Acc_inv H _))).
  apply Nat.leb_gt in e.
  apply Nat.sub_lt; auto with *.
Defined.  (* Compute (f' 10 (lt_wf 10)). doesn't evaluate to a nat if ended with Qed. *)

无论如何,我的问题仍然存在Z.

Fixpoint g' (n:Z) (H: Acc (Zwf 0) n) : Z.
  refine (if dec (n<=?0) then 0 else n + (g' (n-1) (Acc_inv H _))).
  split; now apply Z.leb_gt in e; auto with *.
Defined.

Compute (g' 10 (Zwf_well_founded 0 10)).

Making Zwf_well_founded https://coq.inria.fr/library/Coq.ZArith.Zwf.html#Zwf_well_founded透明没有帮助,因为它在标准库中的定义方式:

Lemma Zwf_well_founded : well_founded (Zwf c).
...
    case (Z.le_gt_cases c y); intro; auto with zarith.
...
Qed.

如果将上面证明中的行替换为

     case (Z_le_gt_dec c y); intro; auto with zarith.

并替换Qed. with Defined.(你已经这样做了)一切都应该有效。这是因为原始证明依赖于逻辑项,并且这阻止了评估者进行模式匹配,因为逻辑实体Z.le_gt_cases是不透明的,而计算实体Z_le_gt_dec是透明的。看在愤怒中使用 Coq 的评估机制 http://gallium.inria.fr/blog/coq-eval/泽维尔·勒罗伊 (Xavier Leroy) 的博客文章。您可能还会发现有用Qed 被认为是有害的 https://gmalecha.github.io/reflections/2017/qed-considered-harmful格雷戈里·马莱查 (Gregory Malecha) 发表的文章。

而不是修改证明Zwf_well_founded你可以重复使用Zlt_0_rec https://coq.inria.fr/library/Coq.ZArith.Wf_Z.html#Zlt_0_rec像这样:

Require Import Coq.ZArith.ZArith.

Open Scope Z.

Definition H (x:Z) (H_rec : (forall y : Z, 0 <= y < x -> Z)) (nonneg : 0 <= x) : Z.
  refine (if Z_zerop x then 0 else x + (H_rec (Z.pred x) _ )).
  auto with zarith.
Defined.

Definition h (z : Z) : Z :=
  match Z_lt_le_dec z 0 with left _ => 0 | right pf => (Zlt_0_rec _ H z pf) end.

Check eq_refl : h 100 = 5050.

这有点不太方便,因为现在我们必须处理负数h.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Coq 无法在 Z 上计算有根据的函数,但它可以在 nat 上运行 的相关文章

  • 递归分割列表函数 LISP

    split list 函数接受一个列表并返回一个由两个列表组成的列表 其中两个列表由输入的交替元素组成 我写了以下内容 defun split list L cond endp L list NIL NIL t let X split li
  • 如何在C中递归地找到另一个字符串中的字符串位置?

    我们有一个任务来创建带有两个字符串参数的递归函数 原型应该是这样的 int instring char word char sentence 如果我们愿意调用函数 instring Word Another Word 它应该具有以下返回值
  • 从数据库结果生成多维数组的递归函数

    我正在编写一个函数 它接受页面 类别数组 来自平面数据库结果 并根据父 ID 生成嵌套页面 类别项目数组 我想递归地执行此操作 以便可以完成任何级别的嵌套 例如 我在一个查询中获取所有页面 这就是数据库表的样子 id parent id t
  • 递归:n项级数之和

    需要递归函数 系列是 1 2 3 3 4 5 4 5 6 7 递归求 n 的级数之和 我无法想到应该在函数中传递哪些参数 我的方法 我认为我应该传递 n 要相乘的项数 但我无法想到的是我应该如何在同一个函数中 和 以及我的 return 语
  • python解释器自动重启而不返回答案

    调用递归函数时 python解释器会自动重新启动吗 我正在编写一个快速排序算法 并尝试对一个大的数字数组 顺序 10 4 进行排序 但是当我尝试对整个数组进行排序时 python 正在重新启动 即给我 重新启动 并且存储在内存中的所有值 函
  • 地形/山地算法未按预期工作

    我想使用一个非常基本的原理创建一个上面有山的地形 如以下高度图所示 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0
  • 如何使用二叉树中的递归来完成回溯

    我正在尝试插入一个二进制节点 我的代码很复杂 没有希望挽救它 所以我计划重写它 基本上我没有考虑回溯 也没有仔细考虑算法 我正在尝试使用顺序遍历插入二进制节点 但我不明白应该如何回溯 D B E A C F 我如何搜索根 D 的左子树 然后
  • 你能快速告诉我这个伪代码是否有意义吗?

    我相信我的代码现在是万无一失的 我现在将写出伪代码 但我确实有一个问题 为什么 DRJava 要求我返回 if 语句之外的内容 正如你所看到的 我为 ex 写了 return 1 只是因为它问了 但是它永远不会返回该值 谁可以给我解释一下这
  • 变量的多个值介于 0 和数字序言之间

    所以我一直在尝试自学序言 我认为我进展顺利 然而 我有点坚持我正在尝试的这一种方法 toN N A A 等于 0 到 N 1 之间的整数值 按升序生成 所以 toN 5 A 将是 A 0 A 1 A 2 A 3 A 4 我对序言还很陌生 所
  • 如何在 Alloy 中构建递归谓词/函数

    我试图在 Alloy 中生成两组类 例如重构之前的类 重构应用程序后的应用程序和类 假设在第一组中我们有以下类 ALeft gt BLeft gt CLeft Class1 Class2 gt Class3 gt Class4 这意味着 A
  • 如何将嵌套对象数组转换为 CSV?

    我有一个包含嵌套对象的数组 例如 name 1 children name 1 1 children 1 2 id 2 thing name 2 1 children 2 2 name 3 stuff name 3 1 children 3
  • 将括号子集映射到字符

    我正在尝试创建一个 Scala 方法 该方法将采用一个父括号组 表示为字符串 然后将每个括号子组映射到不同的字母 然后它应该将它们放入它返回的映射中 所以基本上我调用以下方法 如下所示 val s 2 x 3 6 val map mapPa
  • 使用线性递归计算第 n 个斐波那契数 [重复]

    这个问题在这里已经有答案了 我努力了二元递归找到第 n 个斐波那契数 或通过使用整个斐波那契数列 for循环进入main 但根据Java 中的数据结构和算法 第六版 迈克尔 T 古德里奇 这是一种效率极低的方法 因为它需要对该方法进行指数级
  • e:B, f:(B,A)=>B) : B 是什么意思

    我对这意味着什么感到困惑 我理解柯里化 但我似乎无法完全阅读代码 def foldLeft A B xs List A e B f B A gt B B 只是几个建议 顺便说一句 里面没有柯里化 def foldLeft A B xs Li
  • Clojure 尾递归与质因数

    我正在尝试自学 clojure 并使用 Prime Factors Kata 和 TDD 的原则来实现这一目标 通过一系列 Midje 测试 如下所示 fact primefactors 1 gt list fact primefactor
  • Python:打印自定义异常时超出最大递归深度

    下面的代码抛出RuntimeError maximum recursion depth exceeded while getting the str of an object 我可以用两种不同的方式解决无限递归 但我不明白为什么每个修复都有
  • 从原点开始在离散 2D 网格上迭代向外螺旋的算法

    例如 这是预期螺旋的形状 以及迭代的每个步骤 y 16 15 14 13 12 17 4 3 2 11 18 5 0 1 10 x 19 6 7 8 9 20 21 22 23 24 其中线条是 x 轴和 y 轴 以下是算法每次迭代 返回
  • 获取嵌套数组 JS 中对象的所有父对象

    我在使用 vuejs 的项目上遇到问题 我有一个像这样的嵌套对象数组 Data data id 1 parent id null title First folder children id 3 parent id 1 title Firs
  • 查找并打印 x1+x2+x3=num 的解数

    我需要写一个recusive接收整数的函数num并返回方程 的解数 x1 x2 x3 num where x1 x2 x3是 1 10 之间的数字 该方法应打印所有解决方案 例如如果num 3然后该方法将打印1 1 1并将返回1 if nu
  • 使用递归返回嵌套列表中第二小的数字

    我必须归还第二小的使用递归的 python 列表中的数字 以及no loops 我所做的是创建一个辅助函数 它返回列表中 最小 第二小的 值的元组 然后我只取tuple 1 in my second smallest func def s

随机推荐

  • 在 ReactJS 中重定向到上一页

    自从我进行检查后 我在重定向到上一页时遇到问题isLoggedIn 现在的问题是检查后isLoggedIn它重定向到默认路由 如何维护我所在的页面 我现在所做的是使用referer但它是未定义的 请帮我找到另一种方法 请检查我的代码如下 L
  • 从模型状态验证中删除对象

    我有两个模型 public class UserInfo public long ID get set Required StringLength 50 public string FirstName get set public bool
  • 如何获取matplotlib中的图例位置

    我正在尝试获取 matplotlib 中的图例位置 似乎 Legend get window extent 应该提供此功能 但无论图例位于何处 它都会返回相同的值 这是一个例子 from matplotlib import pyplot a
  • 异常后重试操作:请批评我的代码

    我的 Perl 应用程序使用的资源有时会暂时不可用 从而导致异常die 最值得注意的是 它访问由多个线程共享的 SQLite 数据库 并通过以下方式与其他应用程序共享 DBIx Class 每当发生此类异常时 应重试该操作 直到达到超时为止
  • 使 ViewPager 的高度等于 PagerAdapter 中最高项目的高度

    我有一个ViewPager并用它在视图之间滑动而不是 Fragments 当我给View Pagerwrap content 高度 它不显示任何内容 所以我必须给它一个固定的高度 但我遇到了另一个问题 当项目的高度大于固定高度时 视图无法正
  • 具有默认实现的接口和抽象类有什么区别? [复制]

    这个问题在这里已经有答案了 C 8 0 引入了一项新的语言功能 接口成员的默认实现 public interface IRobot void Talk string message Debug WriteLine message 新的默认接
  • 如何从 std::string 获取可写的 C 缓冲区?

    我正在尝试使用 MFC 移植我的代码CString to std string适用于微软Windows平台 我对某件事很好奇 在下面的例子中说 CString MakeLowerString LPCTSTR pStr CString str
  • 无法将下一个js部署到azure

    我正在尝试将我的 NEXTJS 应用程序部署到 azure 我使用安装了 Node 的 Linux 操作系统创建了一个 Web 应用程序 我的package json看起来像这样 name frontend version 1 0 0 de
  • 使用同一个ajax调用打开多个动态链接

    我正在显示多个使用相同的动态链接 ajax加载第一个链接上的内容很好 但不适用于其余链接 如何让它加载同一div中其他链接的内容 Html string a href link name name a div div Jquery href
  • 使用 GoogleMap 或 MapBox Direction API 在我的应用程序中实现我自己的导航

    我想在我的 Android 应用程序中为驾驶员实现导航地图 我不想使用 URL 方案打开 google 地图应用程序来导航 我更喜欢在我的应用程序中实现此导航功能 就像 Google 地图一样 我的要求很简单 将用户从一个地方导航到另一个地
  • shouldComponentUpdate 并非从未被调用

    请看一下我的代码 我尝试限制给定无状态组件的重新渲染 但这样做发现 shouldComponentUpdate 永远不会被调用 我已经从 styledComponents 中删除了包装器 之前有人报道过这种情况 但仍然绝对没有被调用 除此之
  • 在 JavaScript 中迭代带有“洞”的数组

    我有一个数组 其中一些项目将被删除 但有些循环仍在运行 所以我想简单地跳过删除对象的地方 我知道 for i in array 的语法应该执行此操作 因为它会迭代索引 但是我应该如何删除我的项目呢 因为当我执行 array 4 null 时
  • 查看pdf时隐藏或修改Webview2的工具栏

    我正在使用新的 Webview2 控件在 WPF 应用程序中呈现 Pdf 文件 这运行良好 但我想自定义工具栏以隐藏例如某些条件的保存按钮 我没有找到直接从 Webview2 CoreWebView2 对象执行此操作的方法或属性 但是 如果
  • 尝试调用自定义过滤器会导致“错误 TS2349:无法调用类型缺少调用签名的表达式”

    我试图从 Angular 控制器调用自定义过滤器 但收到错误 无法调用类型缺少调用签名的表达式 我在我从事的上一个项目中是这样实现的 所以我不知道哪里出了问题 此时过滤器不包含任何逻辑 因为我需要先编译它 这是过滤器
  • 用带孔的多边形制作 sf 对象并设置 crs

    With contourLines 我已经提取了数据的 95 轮廓 我想用正确的 crs 制作一个 sf 对象 虽然我无法分享我的实际数据集 但我改编了一个示例SO post https stackoverflow com question
  • Codeigniter ajax使用ajax代码将数据发送到控制器

  • 如何在 WinRT 8.1 上 P/调用 kernel32.dll

    我正在尝试使用本机 API 方法 GetNativeSystemInfo 在 Windows 8 1 上标记为支持手机和桌面应用商店应用程序 在文档中 它被列为存在于 kernel32 dll 中 伟大的 所以我对 P Invoke 的第一
  • Android:如何使用 AlarmManager

    我需要在 20 分钟后触发一段代码AlarmManager正在设置 有人可以向我展示有关如何使用的示例代码吗AlarmManager在 Android 中 我已经研究了一些代码几天了 但它不起作用 一些示例代码 并不是那么容易AlarmMa
  • 停止运行 PHP 服务器,命令行

    所以我已经做到了php S localhost 8000 但我不再需要它了 我需要找回我的 8000 localhost 如何停止php服务器 killall 9 php 我就是这么做的
  • Coq 无法在 Z 上计算有根据的函数,但它可以在 nat 上运行

    我正在 为我自己 写一篇关于如何在 Coq 中进行有根据的递归的解释 参见 Coq Art 书 第 15 2 章 首先我做了一个基于的示例函数nat效果很好 但后来我又做了一次Z 当我使用Compute来评估它 它并没有一直降低到Z价值 为