2014-10-05 2 views
2

Вот определение полиморфных бинарных деревьев, которые я использую в проекте Coq.Определение древовидной формы в виде камней в 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 

возможно ли это?

+0

Можете ли вы пояснить немного больше, как мы должны интерпретировать это обозначение?Я думаю, что возможно сделать то, что вы хотите, хотя и не только с обозначениями. –

+0

'' 'следует понимать как префиксный оператор, который принимает 2 аргумента и возвращает дерево с первым и вторым аргументами в качестве левого и правого дочерних узлов. – user287393

ответ

4

Я попытался получить решение, которое использовало только обозначения Coq, но не могло заставить его работать. Я подозреваю, что расширяемый парсер Coq недостаточно силен, чтобы понять нужную вам нотацию. Однако существует решение бедного человека, которое включает зависимые типы. Идея состоит в том, чтобы написать парсер для этой нотации и использовать этот синтаксический анализатор для кодирования состояния парсера. Тип говорит, что синтаксический анализатор «читает» некоторый токен (фактически, берет этот токен в качестве аргумента для вызова функции) и переходит в следующее состояние: зависит от токена, который он только что прочитал.

Однако есть небольшая тонкость, которая заключается в том, что нельзя писать этот тип, используя только обычные типы функций Coq, потому что количество аргументов, которые будут выполняться, будет зависеть от всех аргументов, к которым оно применяется. Одним из решений является использование coinductive типа для кодирования такое поведение, объявляя принуждение, чтобы сделать его похожим на функции:

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 с упрощенной версией этой техники, сделанной более общей.