binary-tree - 在 Coq 中定义 Unlambda 风格的树表示法

标签 binary-tree parentheses coq notation

这是我在 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

这可能吗?

最佳答案

我试图找到一个仅使用 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 调用的原因。这可能不像真正的符号那么实用,而且定义确实有点棘手,但在某些情况下可能非常有用。

这是一个gist与整个 .v 文件。

更新:我写了一个post该技术的简化版本变得更加通用。

关于binary-tree - 在 Coq 中定义 Unlambda 风格的树表示法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26208107/

相关文章:

c++ - 关于c++中的括号

coq - 宇宙不一致(因为严格的正性限制?)

c - XML 是否存在任何二进制替代方案

c++ - 当我尝试在 main 中调用插入函数时,它没有取数字?

java - 二叉树的高度 StackOverflow 异常

haskell - 在 Haskell 中使用尾递归拆分 BinTree

haskell - Haskell类型签名中括号的含义是什么?

visual-studio-2012 - 如何在 Visual Studio 2012 for C# 中启用括号补全?

coq - Ltac 调用 "cofix"失败。错误 : All methods must construct elements in coinductive types

coq - 对 Coq 中的两个子目标使用相同的证明