Các định nghĩa đệ quy trên một loại quy nạp với các thành phần lồng nhau


21

Xem xét một loại quy nạp có một số lần xuất hiện đệ quy trong một vị trí lồng nhau, nhưng hoàn toàn tích cực. Ví dụ, các cây có phân nhánh hữu hạn với các nút sử dụng cấu trúc dữ liệu danh sách chung để lưu trữ các con.

Inductive LTree : Set := Node : list LTree -> LTree.

Cách ngây thơ trong việc xác định hàm đệ quy trên các cây này bằng cách đệ quy trên các cây và danh sách các cây không hoạt động. Đây là một ví dụ với sizehàm tính toán số lượng nút.

Fixpoint size (t : LTree) : nat := match t with Node l => 1 + (size_l l) end
with size_l (l : list LTree) : nat := match l with
    | nil => 0
    | cons h r => size h + size_l r
  end.

Định nghĩa này không đúng định dạng (thông báo lỗi được trích):

Error:
Recursive definition of size_l is ill-formed.
Recursive call to size has principal argument equal to
"h" instead of "r".

Tại sao định nghĩa không được định hình, mặc dù rrõ ràng là một phần phụ của l? Có cách nào để xác định các hàm đệ quy trên cấu trúc dữ liệu như vậy không?


Nếu bạn không thành thạo cú pháp Coq: LTreelà loại quy nạp tương ứng với ngữ pháp sau.

LTree::=|list(LTree)

Chúng tôi cố gắng xác định sizechức năng bằng cách cảm ứng trên cây và danh sách. Trong OCaml, đó sẽ là:

type t = Node of t list
let rec size = function Node l -> 1 + size_l l
and size_l = function [] -> 0
                    | h::r -> size h + size_l r


Bạn có thể thêm các định nghĩa hàm tương đương trong một cái gì đó ít Coqy và toán học hơn không? Tôi gặp khó khăn trong việc hiểu cú pháp.
Raphael

1
@Raphael Tôi đã thử, bây giờ có tốt hơn không? Thành thật mà nói, câu hỏi này khá cụ thể đối với Coq.
Gilles 'SO- đừng trở nên xấu xa'

Câu trả lời:


14

Những gì hoạt động

Nếu bạn lồng định nghĩa của điểm cố định vào danh sách bên trong định nghĩa của điểm cố định trên cây, kết quả sẽ được gõ tốt. Đây là một nguyên tắc chung khi bạn đã đệ quy lồng nhau theo kiểu quy nạp, tức là khi đệ quy đi qua một hàm tạo như thế nào list.

Fixpoint size (t : LTree) : nat :=
  let size_l := (fix size_l (l : list LTree) : nat :=
                  match l with
                    | nil => 0
                    | h::r => size h + size_l r
                  end) in
  match t with Node l =>
    1 + size_l l
  end.

Hoặc nếu bạn thích viết điều này chặt chẽ hơn:

Fixpoint size (t : LTree) : nat :=
  match t with Node l =>
    1 + (fix size_l (l : list LTree) : nat :=
          match l with
            | nil => 0
            | h::r => size h + size_l r
          end) l
  end.

(Tôi không biết tôi đã nghe ai từ đầu; điều này chắc chắn được phát hiện độc lập nhiều lần.)

Một vị ngữ đệ quy chung

Tổng quát hơn, bạn có thể định nghĩa nguyên tắc cảm ứng phù hợp của Wap trên LTreethủ công. Nguyên tắc cảm ứng được tạo tự động LTree_rectbỏ qua giả thuyết trong danh sách, bởi vì trình tạo nguyên lý cảm ứng chỉ hiểu các trường hợp tích cực nghiêm trọng không lồng nhau của loại quy nạp.

LTree_rect = 
fun (P : LTree -> Type) (f : forall l : list LTree, P (Node l)) (l : LTree) =>
match l as l0 return (P l0) with
| Node x => f x
end
     : forall P : LTree -> Type,
       (forall l : list LTree, P (Node l)) -> forall l : LTree, P l

Hãy thêm giả thuyết cảm ứng vào danh sách. Để thực hiện nó trong cuộc gọi đệ quy, chúng ta gọi nguyên tắc cảm ứng danh sách và truyền cho nó nguyên tắc cảm ứng cây trên cây nhỏ hơn trong danh sách.

Fixpoint LTree_rect_nest (P : LTree -> Type) (Q : list LTree -> Type)
                         (f : forall l, Q l -> P (Node l))
                         (g : Q nil) (h : forall t l, P t -> Q l -> Q (cons t l))
                         (t : LTree) :=
  match t as t0 return (P t0) with
    | Node l => f l (list_rect Q g (fun u r => h u r (LTree_rect_nest P Q f g h u)) l)
  end.

Tại sao

Câu trả lời tại sao nằm trong các quy tắc chính xác để chấp nhận các hàm đệ quy. Các quy tắc này là lực lượng tinh vi, bởi vì có một sự cân bằng tinh tế giữa việc cho phép các trường hợp phức tạp (chẳng hạn như trường hợp này, với đệ quy lồng nhau trong kiểu dữ liệu) và sự không rõ ràng. Tài liệu tham khảo Coq giới thiệu ngôn ngữ (tính toán của các cấu trúc quy nạp, là ngôn ngữ chứng minh của Coq), chủ yếu là với các định nghĩa chính xác, nhưng nếu bạn muốn các quy tắc chính xác về quy nạp và cưỡng chế, bạn sẽ cần đến các tài liệu nghiên cứu, về chủ đề này của Eduardo Giménez [1].

FixFixfi{f1:A1:=t1;f2:A2:=t2}

Γ1=(x:LTree)A1=natt1=case(x,LTree,λy.g1(f2y))Γ2=(l:listLTree)A2=natt2=case(l,listLTree,λhr.g2(f1h)(f2r))

fjtifi

  • i=1j=2ltsize
  • i=2j=1hlsize_l
  • i=2j=2rlsize_l

Lý do tại sao hcấu trúc không nhỏ hơn ltheo thông dịch viên Coq là không rõ ràng đối với tôi. Theo như tôi hiểu từ các cuộc thảo luận trong danh sách câu lạc bộ Coq [1] [2], đây là một hạn chế trong trình thông dịch, về nguyên tắc có thể được gỡ bỏ, nhưng rất cẩn thận để tránh đưa ra sự không nhất quán.

Tài liệu tham khảo

Cocorico, wiki Coq đang thịnh hành: Cảm ứng lẫn nhau

Danh sách gửi thư của Coq-Club:

Nhóm phát triển Coq. Trợ lý bằng chứng Coq: Tài liệu tham khảo . Phiên bản 8.3 (2010). [ web ] ch. 4 .

Eduardo Giménez. Mã hóa các định nghĩa được bảo vệ với các sơ đồ đệ quy . Trong các loại'94: Các loại bằng chứng và chương trình , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]

Eduardo Giménez. Định nghĩa đệ quy cấu trúc trong lý thuyết loại . Trong ICALP'98: Kỷ yếu của Hội thảo quốc tế lần thứ 25 về Automata, Ngôn ngữ và Lập trình. Springer-Verlag, 1998. [ PDF ]


7

Đây rõ ràng là một vấn đề cụ thể đối với Coq vì tôi tin rằng có nhiều cách tốt hơn để giải quyết vấn đề này với một số trợ lý chứng minh khác (tôi đang xem Agda)

Lúc đầu, tôi nghĩ rằng rnó không được công nhận là nhỏ hơn về mặt cấu trúc bởi vì cấu trúc chỉ nói về định nghĩa quy nạp hiện đang được xử lý bởi Fixpoint: vì vậy đây không phải là một bộ LTreecon ngay cả khi nó là một bộ listcon.

Nhưng nếu bạn mở rộng việc xử lý danh sách, thì nó hoạt động:

Fixpoint size t :=
  match t with
  | Node l => S
     ((fix size_l l :=
     match l with
     | nil => 0
     | cons t l => size_l l + size t
     end) l)
 end.

Hoặc do chức năng phụ trợ đã tồn tại trong thư viện chuẩn:

Require Import List.

Fixpoint size t :=
  match t with
  | Node l => S (fold_left (fun a t => a + size t) l 0)
  end.

Thành thật mà nói tôi không chắc tại sao những điều đó được Coq chấp nhận, nhưng tôi chắc chắn rất vui vì họ như vậy.

Ngoài ra còn có một giải pháp hoạt động thường xuyên hơn và không chỉ cho các danh sách: xác định loại quy nạp độc lập. Trong trường hợp này, bạn có thể xác định sizechức năng của mình bằng tay. (với hai điểm cố định)

Inductive LTree : Set :=
  | Node : list_LTree -> LTree
with list_LTree : Set :=
  | LTree_nil : list_LTree
  | LTree_cons : LTree -> list_LTree -> list_LTree.

Lưu ý rằng nếu bạn gặp vấn đề với các định nghĩa quy nạp phức tạp hơn, bạn có thể sử dụng đối số giảm kích thước. Điều đó là có thể nhưng cồng kềnh cho vấn đề này (và tôi dám nói cho hầu hết các vấn đề)


Điều tôi vẫn không hiểu ngày hôm nay là tại sao cách tiếp cận ngây thơ không hiệu quả. Về nguyên tắc, điều này sẽ là hậu quả của bài viết của Eduardo Giménez, nhưng tôi không thấy nơi khấu trừ; đây có thể là một hạn chế của công cụ Coq hơn là tính toán cơ bản.
Gilles 'SO- ngừng trở thành ác quỷ'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.