【问题标题】:Defining Unlambda-style tree notation in Coq在 Coq 中定义 Unlambda 风格的树表示法
【发布时间】:2014-10-05 23:00:58
【问题描述】:

这是我在 Coq 项目中使用的多态二叉树的定义。

Inductive tree { X : Type } : Type :=
  | t_a : X -> tree
  | t_m : tree -> tree -> tree.

使用此定义声明的自然数二叉树( 1 ( ( 2 3 ) 4 ) ) 将是:

t_m ( t_a 1 ) ( t_m ( t_m ( t_a 2 ) ( t_a 3 ) ) ( t_a 4 ) ) 

如您所见,随着叶子数量的增加,定义很快变得无法使用。我想要做的是为树定义一个 Unlambda 风格的符号,这样我就可以用

' 1 ' ' 2 3 4

这可能吗?

【问题讨论】:

  • 你能再澄清一下我们应该如何解释这个符号吗?我认为有可能做你想做的事,虽然不只是用符号。
  • ' 应该理解为一个前缀运算符,它接受2个参数,返回一个以第一个参数和第二个参数为左右子节点的树。

标签: binary-tree parentheses coq notation


【解决方案1】:

我试图获得一个仅使用 Coq 表示法的解决方案,但无法使其工作。我怀疑 Coq 的可扩展解析器不够强大,无法理解您想要的符号。然而,有一个穷人的解决方案涉及依赖类型。这个想法是为该符号编写一个解析器,并使用该解析器的类型来编码解析器状态。该类型表示解析器“读取”了某个标记(实际上,将该标记作为函数调用的参数),然后进入下一个状态,取决于它刚刚读取的标记。 p>

不过,有一点微妙之处,那就是不能仅使用常规 Coq 函数类型来编写该类型,因为该函数将采用的参数数量取决于它所应用的所有参数。一种解决方案是使用协归纳类型来编码这种行为,声明一个强制使其看起来像一个函数:

Inductive tree (X : Type) : Type :=
  | t_a : X -> tree X
  | t_m : tree X -> tree X -> tree X.

Arguments t_a {X} _.
Arguments t_m {X} _ _.

CoInductive tree_builder X : nat -> Type :=
| TbDone : tree X -> tree_builder X 0
| TbRead : forall n, (forall o : option X, tree_builder X match o with
                                                          | Some x => n
                                                          | None => S (S n)
                                                          end) ->
                     tree_builder X (S n).

Arguments TbDone {X} _.
Arguments TbRead {X} _ _.

(* Destructors for tree_builder *)

Definition case0 {X} (x : tree_builder X 0) : tree X :=
  match x with
  | TbDone t => t
  end.

Definition caseS {X n} (x : tree_builder X (S n)) :
  forall o : option X, tree_builder X match o with
                                      | Some x => n
                                      | None => S (S n)
                                      end :=
  match x with
  | TbRead _ f => f
  end.

Definition tb X n := tree_builder X (S n).

(* force is what does the magic here: it takes a tb and coerces it to a
   function that may produce another tb, depending on what it is applied to. *)

Definition force X n (x : tb X n) : forall o : option X,
                                      match o with
                                      | Some x =>
                                        match n with
                                        | 0 => tree X
                                        | S n' => tb X n'
                                         end
                                      | None =>
                                        tb X (S n)
                                      end :=
  fun o =>
    match o return tree_builder X match o with
                                  | Some x => n
                                  | None => S (S n)
                                  end ->
                   match o with
                   | Some x => match n with
                               | 0 => tree X
                               | S n' => tb X n'
                               end
                   | None => tb X (S n)
                   end
    with
    | Some x => match n return tree_builder X n -> match n with
                                                   | 0 => tree X
                                                   | S n' => tb X n'
                                                   end
                with
                | 0 => fun t => case0 t
                | S _ => fun t => t
                end
    | None => fun t => t
    end (caseS x o).

Coercion force : tb >-> Funclass.

那么,我们的解析器只是tb X 0 类型的术语。正如通常所做的那样,由于参数数量可变,因此必须以连续传递样式编写。

Fixpoint parser_cont_type X (n : nat) : Type :=
  match n with
  | 0 => tree X
  | S n' => tree X -> parser_cont_type X n'
  end.

CoFixpoint parser X n : parser_cont_type X n -> tree_builder X n :=
  match n with
  | 0 => fun k => TbDone k
  | S n' => fun k : tree X -> parser_cont_type X n' =>
              TbRead n' (fun o => match o return tree_builder X match o with
                                                                | Some _ => n'
                                                                | None => S (S n')
                                                                end
                               with
                               | Some x => parser X n' (k (t_a x))
                               | None => parser X (S (S n')) (fun (t1 t2 : tree X) => k (t_m t1 t2))
                               end)
  end.

Definition parser' X : tb X 0 :=
  parser X 1 (fun t => t).

接下来,我们可以定义一些额外的符号以使其更易于使用:

Notation "[ x ]" := (Some x) (at level 0).
Notation "''" := None (at level 0).
Notation "!" := (parser' _) (at level 20).

这是编写示例树的方法,例如:

Definition my_tree : tree nat := Eval hnf in ! '' [1] '' '' [2] [3] [4].

注意初始的! 开始调用解析器,以及标记叶子所需的[]。我也无法让 Coq 的解析器自己接受 ' 作为令牌。但是,除了这些小细节之外,它与您所拥有的非常接近。

一个问题是,由于解析器是使用 Coq 函数定义的,因此需要进行一些简化才能得到一个与您最初的术语完全相同的术语。这就是我在定义上添加Eval 调用的原因。这可能不如真正的符号实用,而且定义确实有点棘手,但在某些情况下它可能非常有用。

这是带有整个 .v 文件的 gist

更新:我已经写了一个post,其中包含了这种技术的简化版本,使其更通用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多