Definisi rekursif atas tipe induktif dengan komponen bersarang

21

Pertimbangkan jenis induktif yang memiliki beberapa kejadian rekursif di lokasi bersarang, tetapi sangat positif. Sebagai contoh, pohon dengan cabang terbatas dengan node menggunakan struktur data daftar umum untuk menyimpan anak-anak.

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

Cara naif mendefinisikan fungsi rekursif atas pohon-pohon ini dengan mengulangi pohon dan daftar pohon tidak bekerja. Berikut ini contoh dengan sizefungsi yang menghitung jumlah node.

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.

Definisi ini salah bentuk (pesan kesalahan dikutip):

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

Mengapa definisi ini buruk bentuknya, meskipun rjelas-jelas merupakan bagian dari l? Apakah ada cara untuk mendefinisikan fungsi rekursif pada struktur data seperti itu?


Jika Anda tidak lancar dalam sintaks Coq: LTreeadalah tipe induktif yang sesuai dengan tata bahasa berikut.

LTree::=|list(LTree)

Kami mencoba untuk mendefinisikan sizefungsi dengan induksi lebih dari pohon dan daftar. Dalam OCaml, itu akan menjadi:

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
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Apakah ini sesuai topik? Saya tidak yakin; mari kita bahas ini di Meta .
Gilles 'SO- berhenti bersikap jahat'
Bisakah Anda menambahkan definisi fungsi yang setara dalam sesuatu yang kurang Coqy dan lebih matematika? Saya mengalami kesulitan memahami sintaksis.
Raphael
1
@ Raphael saya sudah mencoba, apakah itu lebih baik sekarang? Jujur, pertanyaan ini cukup spesifik untuk Coq.
Gilles 'SO- stop being evil'

Jawaban:

14

Pekerjaan apa

Jika Anda mencari definisi fixpoint pada daftar di dalam definisi fixpoint pada pohon, hasilnya diketik dengan baik. Ini adalah prinsip umum ketika Anda memiliki rekursi bersarang dalam tipe induktif, yaitu ketika rekursi melalui konstruktor suka 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.

Atau jika Anda lebih suka menulis ini lebih singkat:

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.

(Saya tidak tahu dari siapa saya mendengarnya dari awal; ini tentu saja ditemukan secara independen berkali-kali.)

Predikat rekursi umum

Secara umum, Anda dapat mendefinisikan prinsip induksi "benar" LTreesecara manual. Prinsip induksi yang dihasilkan secara otomatis LTree_rectmenghilangkan hipotesis dalam daftar, karena generator prinsip induksi hanya memahami kejadian non-bersarang yang benar-benar positif dari tipe induktif.

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

Mari kita tambahkan hipotesis induksi pada daftar. Untuk memenuhinya dalam panggilan rekursif, kami memanggil prinsip induksi daftar dan memberikannya prinsip induksi pohon pada pohon kecil di dalam daftar.

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.

Mengapa

Jawaban untuk alasan terletak pada aturan yang tepat untuk menerima fungsi rekursif. Aturan-aturan ini sangat halus, karena ada keseimbangan yang rumit antara memungkinkan kasus-kasus yang kompleks (seperti yang ini, dengan rekursi bersarang dalam tipe data) dan tidak sehat. The referensi Coq panduan memperkenalkan bahasa (kalkulus konstruksi induktif, yang merupakan bahasa bukti Coq), sebagian besar dengan definisi formal yang tepat, tetapi jika Anda ingin aturan yang tepat tentang induksi dan coinduction Anda akan perlu pergi ke makalah penelitian, tentang topik ini Eduardo Giménez [1].

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

Γ1=(x:L.Tree)SEBUAH1=nSebuahtt1=cSebuahse(x,L.Tree,λy.g1(f2y))Γ2=(l:lsayastL.Tree)SEBUAH2=nSebuahtt2=cSebuahse(l,lsayastL.Tree,λhr.g2(f1h)(f2r))

fjtsayafsaya

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

Alasan mengapa hsecara struktural lebih kecil daripada lmenurut penerjemah Coq tidak jelas bagi saya. Sejauh yang saya mengerti dari diskusi pada daftar Coq-club [1] [2], ini adalah batasan dalam juru bahasa, yang pada prinsipnya dapat diangkat, tetapi dengan sangat hati-hati untuk menghindari memperkenalkan ketidakkonsistenan.

Referensi

Cocorico, Coq wiki nonterminating: Saling Induksi

Milis Coq-Club:

Tim Pengembangan Coq. Asisten Bukti Coq: Manual Referensi . Versi 8.3 (2010). [ web ] ch. 4 .

Eduardo Giménez. Mengkodifikasi definisi yang dijaga dengan skema rekursif . Dalam Types'94: Jenis untuk Bukti dan Program , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]

Eduardo Giménez. Definisi Rekursif Struktural dalam Teori Tipe . Dalam ICALP'98: Prosiding Kolokium Internasional ke 25 tentang Automata, Bahasa dan Pemrograman. Springer-Verlag, 1998. [ PDF ]

Gilles 'SANGAT berhenti menjadi jahat'
sumber
7

Ini jelas merupakan masalah khusus untuk Coq karena saya percaya ada cara yang lebih baik untuk mengatasinya dengan beberapa asisten bukti lainnya (Saya sedang melihat Agda)

Pada awalnya saya pikir rtidak dikenali secara struktural lebih kecil karena strukturnya hanya tentang definisi induktif yang saat ini ditangani oleh Fixpoint: jadi ini bukan LTreesubterm walaupun itu adalah listsubterm.

Tetapi jika Anda memperluas pemrosesan daftar, maka itu berfungsi:

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.

Atau karena fungsi bantu sudah ada di perpustakaan standar:

Require Import List.

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

Sejujurnya saya tidak yakin mengapa itu diterima oleh Coq, tapi saya senang mereka.

Ada juga solusi yang bekerja lebih sering dan tidak hanya untuk daftar: mendefinisikan tipe induktif mandiri. Dalam hal ini Anda dapat mendefinisikan sizefungsi Anda secara manual. (dengan dua fixpoints)

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

Perhatikan bahwa jika Anda memiliki masalah untuk definisi induktif yang lebih kompleks, Anda dapat menggunakan argumen penurunan ukuran. Itu mungkin tetapi rumit untuk masalah ini (dan saya berani mengatakan untuk sebagian besar masalah)

jmad
sumber
Apa yang saya masih tidak mengerti hari ini adalah mengapa pendekatan naif tidak berhasil. Pada prinsipnya, ini harus menjadi konsekuensi dari makalah Eduardo Giménez, tetapi saya tidak melihat di mana deduksi terputus; ini mungkin merupakan batasan mesin Coq daripada kalkulus yang mendasarinya.
Gilles 'SO berhenti makhluk jahat'