【问题标题】:Cannot guess decreasing argument of fix for nested match in Coq无法猜测 Coq 中嵌套匹配修复的递减参数
【发布时间】:2017-05-20 17:42:50
【问题描述】:

我对术语有以下定义:

Inductive term : Type :=
  | Var : variable -> term
  | Func : function_symbol -> list term -> term.

和一个函数pos_list 获取术语列表并返回每个子术语的“位置”列表。例如对于[Var "e"; Func "f" [Var "x"; Func "i" [Var "x"]]],我应该得到[[1]; [2]; [2; 1]; [2; 2]; [2; 2; 1]],其中每个元素代表子项树层次结构中的一个位置。

Definition pos_list (args:list term) : list position :=
  let fix pos_list_aux ts is head :=
    let enumeration := enumerate ts in
      let fix post_enumeration ts is head :=
        match is with
        | [] => []
        | y::ys =>
          let new_head := (head++y) in
          match ts with
          | [] => []
          | (Var _)::xs => [new_head] ++ (post_enumeration xs ys head)
          | (Func _ args')::xs =>
            [new_head] ++
            (pos_list_aux args' [] new_head) ++
            (post_enumeration xs ys head)
          end
        end
      in post_enumeration ts enumeration head
  in pos_list_aux args [] [].

使用上面的代码我得到了错误

错误:无法猜测fix 的递减参数

在第一个let fix 构造中,但在我看来,对(pos_list_aux args' [] new_head) 的调用(这会导致问题)将args' 作为参数(Func _ args') 的子项,而(Func _ args') 本身就是@987654331 的子项@。

怎么了?

【问题讨论】:

    标签: functional-programming coq totality


    【解决方案1】:

    term 是一个嵌套的归纳类型(因为 list termFunc 构造函数中)并且它经常需要一些额外的工作来向 Coq 解释你的函数是完全的。 CPDT 的 chapter 解释了如何处理这种情况(参见“嵌套归纳类型”部分):

    术语“嵌套归纳类型”暗示了这个特定问题的解决方案。正如相互归纳类型需要相互递归归纳原则一样,嵌套类型也需要嵌套递归。

    这是我为解决您的问题所做的尝试。首先,让我们添加一些导入和定义,以便编译:

    Require Import Coq.Lists.List.
    Import ListNotations.
    Require Import Coq.Strings.String.
    Require Import Coq.Strings.Ascii.
    
    Definition variable := string.
    Definition function_symbol := string.
    Definition position := list nat.
    
    Inductive term : Type :=
      | Var : variable -> term
      | Func : function_symbol -> list term -> term.
    

    我们首先实现一个为单个term 完成工作的函数。请注意,我们定义了一个嵌套函数 pos_list_many_aux,这几乎就是您想要的:

    Definition pos_list_one (i : nat) (t : term) : list position :=
      let fix pos_list_one_aux (i : nat) (t : term) {struct t} : list position :=
          match t with
          | Var _ => [[i]]
          | Func _ args =>
              [i] :: map (cons i)
                         ((fix pos_list_many_aux i ts :=
                             match ts with
                             | [] => []
                             | t::ts =>
                                 pos_list_one_aux i t ++ pos_list_many_aux (S i) ts
                             end) 1 args).     (* hardcoded starting index *)
          end
      in pos_list_one_aux i t.
    

    现在,我们需要一个辅助函数mapi(命名自 OCaml 的标准库)。类似于map,但映射函数也接收当前列表元素的索引。

    Definition mapi {A B : Type} (f : nat -> A -> B) (xs : list A) : list B :=
      let fix mapi i f xs :=
        match xs with
        | [] => []
        | x::xs => (f i x) :: mapi (S i) f xs
        end
      in mapi 0 f xs.
    

    现在一切准备就绪,可以定义pos_list 函数:

    Definition pos_list (args : list term) : list position :=
      concat (mapi (fun i t => pos_list_one (S i) t) args).
    

    让我们运行你的测试:

    Section Test.
      Open Scope string_scope.
    
      Compute pos_list [Var "e"; Func "f" [Var "x"; Func "i" [Var "x"]]].
      (*
       = [[1]; [2]; [2; 1]; [2; 2]; [2; 2; 1]] : list position
       *)
    End Test.
    

    【讨论】:

      【解决方案2】:

      如果你明确告诉 Coq 你正在递归哪个参数,你会得到一条信息略多的错误消息。

      let fix pos_list_aux ts is head {struct ts} :=
      

      现在 Coq 说

      Recursive call to pos_list_aux has principal argument equal to "args'" instead of
      "xs".
      

      如果你改用{struct is},Coq 说

      Recursive call to pos_list_aux has principal argument equal to "[]" instead of
      a subterm of "is".
      

      确定递归是否合理的简单句法规则要求您使用来自使用 match is with ... end 破坏参数的术语进行递归。

      使用从头元素中获取的东西并不是很容易,例如args',甚至在is的递归情况下使用[]。例如,也许您创建了一个无限循环,在其中使用[] 作为递归参数调用自己。类型检查器需要防止这种情况发生。

      句法规则“非常简单”并且在这种情况下并不适用,即使在这种情况下递归“显然”是在结构更小的组件上。 所以你必须以更复杂的方式说服类型检查器args' 是可以的。

      也许其他人可以提供一种优雅的方式来做到这一点?我的第一次尝试是看看Program 是否处理了这个问题(但没有处理)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-24
        • 2019-01-03
        相关资源
        最近更新 更多