5

我对术语有以下定义:

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')本身就是ts.

怎么了 ?

4

2 回答 2

7

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

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

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

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 的 stdlib 中借来的命名)。就像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.
于 2017-05-21T11:57:07.890 回答
1

如果你明确地告诉 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".

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

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

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

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

于 2017-05-21T06:10:43.173 回答