นิยามแบบเรียกซ้ำมากกว่าชนิดอุปนัยที่มีส่วนประกอบซ้อนอยู่


21

พิจารณาประเภทอุปนัยที่มีเกิดขึ้นซ้ำในบางตำแหน่งที่ซ้อนกัน แต่เป็นบวกอย่างเคร่งครัด ตัวอย่างเช่นต้นไม้ที่มีการ จำกัด การแยกด้วยโหนดโดยใช้โครงสร้างข้อมูลรายการทั่วไปเพื่อจัดเก็บเด็ก ๆ

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

วิธีที่ไร้เดียงสาของการกำหนดฟังก์ชั่นแบบเรียกซ้ำผ่านต้นไม้เหล่านี้โดยการเรียกซ้ำกว่าต้นไม้และรายการต้นไม้ไม่ทำงาน นี่คือตัวอย่างของsizeฟังก์ชันที่คำนวณจำนวนโหนด

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.

คำจำกัดความนี้มีรูปแบบไม่ดี (ข้อความที่ตัดตอนมาจากความผิดพลาด):

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

ทำไมคำจำกัดความที่ไม่ถูกต้องถึงแม้ว่าจะrเป็นคำที่ชัดเจนl? มีวิธีการกำหนดฟังก์ชั่นซ้ำในโครงสร้างข้อมูลดังกล่าวหรือไม่?


หากคุณไม่คล่องในไวยากรณ์ Coq: LTreeเป็นประเภทอุปนัยที่สอดคล้องกับไวยากรณ์ต่อไปนี้

LTRอีอี:: =|ล.ผมsเสื้อ(LTRอีอี)

เราพยายามที่จะกำหนดsizeฟังก์ชั่นโดยการเหนี่ยวนำเหนือต้นไม้และรายการ ใน OCaml นั่นจะเป็น:

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

นี่เป็นหัวข้อหรือไม่? ฉันไม่แน่ใจ; ขอหารือเกี่ยวกับเรื่องนี้ Meta
Gilles 'หยุดความชั่วร้าย'

คุณสามารถเพิ่มนิยามฟังก์ชันที่เทียบเท่าได้ในค่า Coqy ที่น้อยลงและ mathy มากกว่าหรือไม่ ฉันมีปัญหาในการเข้าใจไวยากรณ์
Raphael

1
@ ราฟาเอลฉันได้ลองแล้วมันดีขึ้นแล้วเหรอ? สุจริตคำถามนี้ค่อนข้างเฉพาะกับ Coq
Gilles 'หยุดความชั่วร้าย'

คำตอบ:


14

งานอะไร

หากคุณซ้อนนิยามของ fixpoint บนรายการภายในนิยามของ fixpoint บนแผนผังผลลัพธ์จะถูกพิมพ์อย่างดี 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.

หรือถ้าคุณต้องการที่จะเขียนนี้ยิ่งขึ้น:

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.

(ฉันไม่รู้ว่าใครเป็นคนแรกที่ได้ยินฉันค้นพบสิ่งนี้อย่างอิสระหลายครั้ง)

คำกริยาเรียกซ้ำทั่วไป

โดยทั่วไปคุณสามารถกำหนดหลักการอุปนัย "เหมาะสม" LTreeด้วยตนเอง หลักการเหนี่ยวนำที่สร้างขึ้นโดยอัตโนมัติLTree_rectละเว้นสมมติฐานในรายการเนื่องจากเครื่องกำเนิดหลักการเหนี่ยวนำเข้าใจเฉพาะเหตุการณ์เชิงบวกที่ไม่ซ้อนกันอย่างเคร่งครัดของประเภทอุปนัย

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

ลองเพิ่มสมมติฐานอุปนัยในรายการ เพื่อเติมเต็มในการเรียกซ้ำเราเรียกหลักการเหนี่ยวนำรายการและส่งผ่านหลักการเหนี่ยวนำต้นไม้ในต้นไม้ขนาดเล็กภายในรายการ

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.

ทำไม

คำตอบว่าทำไมอยู่ในกฎที่แม่นยำสำหรับการรับฟังก์ชั่นวนซ้ำ กฎเหล่านี้มีความละเอียดอ่อนเนื่องจากมีความสมดุลที่ละเอียดอ่อนระหว่างการอนุญาตกรณีที่ซับซ้อน (เช่นกรณีนี้โดยมีการเรียกซ้ำแบบซ้อนในประเภทข้อมูล) และไม่ถูกผูกมัด อ้างอิง Coq คู่มือแนะนำภาษา (แคลคูลัสของการก่อสร้างอุปนัยซึ่งเป็นภาษาหลักฐานการ Coq) ส่วนใหญ่กับนิยามอย่างเป็นทางการ แต่ถ้าคุณต้องการกฎระเบียบที่แน่นอนเกี่ยวกับการเหนี่ยวนำและ coinduction คุณจะต้องไปที่งานวิจัยที่ ในหัวข้อนี้ Eduardo Giménez's [1]

FixFผมxผม{1:A1=เสื้อ1;2:A2=เสื้อ2}

Γ1=(x:LTRอีอี)A1=naเสื้อเสื้อ1=asอี(x,LTRอีอี,λY.ก.1(2Y))Γ2=(ล.:ล.ผมsเสื้อLTRอีอี)A2=naเสื้อเสื้อ2=asอี(ล.,ล.ผมsเสื้อLTRอีอี,λชั่วโมงR.ก.2(1ชั่วโมง)(2R))

Jเสื้อผมผม

  • ผม=1J=2ltsize
  • ผม=2J=1hlsize_l
  • ผม=2J=2rlsize_l

เหตุผลที่ทำไมhโครงสร้างไม่เล็กกว่าlตามล่าม Coq ไม่ชัดเจนสำหรับฉัน เท่าที่ฉันเข้าใจจากการอภิปรายในรายการ Coq-club [1] [2] นี่เป็นข้อ จำกัด ในล่ามซึ่งในหลักการสามารถยกขึ้นได้ แต่อย่างระมัดระวังเพื่อหลีกเลี่ยงการแนะนำที่ไม่สอดคล้องกัน

อ้างอิง

Cocorico, การทำลายล้าง Coq wiki: การเหนี่ยวนำร่วมกัน

รายชื่อผู้รับจดหมาย Coq-Club:

ทีมพัฒนา Coq Coq ช่วยพิสูจน์: คู่มืออ้างอิง รุ่น 8.3 (2010) [ เว็บ ] ch. 4 .

Eduardo Giménez หมวดหมู่คำจำกัดความกับแผนการรักษา recursive ในTypes'94: ประเภทของการพิสูจน์และโปรแกรม LNCS 996 Springer-Verlag, 1994. ดอย: 10.1007 / 3-540-60579-7_3 [ Springer ]

Eduardo Giménez โครงสร้างการซ้ำคำนิยามในประเภททฤษฎี ในICALP'98: การดำเนินการของการประชุมนานาชาติครั้งที่ 25 เรื่อง Automata, ภาษาและการเขียนโปรแกรม Springer-Verlag, 1998. [ PDF ]


7

เห็นได้ชัดว่านี่เป็นปัญหาเฉพาะของ Coq เนื่องจากฉันเชื่อว่ามีวิธีที่ดีกว่าในการหลีกเลี่ยงปัญหานี้กับผู้ช่วยพิสูจน์คนอื่น ๆ (ฉันกำลังดู Agda)

ตอนแรกฉันคิดrว่าไม่ได้รับการยอมรับว่ามีขนาดเล็กกว่าโครงสร้างเนื่องจากโครงสร้างเป็นเพียงเกี่ยวกับคำจำกัดความอุปนัยที่จัดการในปัจจุบันโดยFixpoint: ดังนั้นนี่จึงไม่ใช่คำLTreeศัพท์แม้ว่าจะเป็นคำlistย่อยก็ตาม

แต่ถ้าคุณขยายการประมวลผลของรายการมันก็ใช้งานได้:

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.

หรือเนื่องจากฟังก์ชันเสริมมีอยู่แล้วในไลบรารีมาตรฐาน:

Require Import List.

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

ความซื่อสัตย์ฉันไม่แน่ใจว่าทำไมพวกเขาถึงได้รับการยอมรับจาก Coq แต่ฉันก็ดีใจที่พวกเขาเป็นเช่นนั้น

นอกจากนี้ยังมีวิธีการแก้ปัญหาที่ทำงานได้บ่อยขึ้นและไม่เพียง แต่สำหรับรายการ: การกำหนดประเภทอุปนัยที่มีอยู่ในตัวเอง ในกรณีนี้คุณสามารถกำหนดsizeฟังก์ชั่นของคุณได้ด้วยตนเอง (มีสองจุดยึด)

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

โปรดทราบว่าหากคุณมีปัญหาสำหรับคำจำกัดความอุปนัยที่ซับซ้อนมากขึ้นคุณสามารถใช้อาร์กิวเมนต์ลดขนาดได้ เป็นไปได้ แต่ยุ่งยากสำหรับปัญหานี้ (และฉันจะกล้าพูดถึงปัญหาส่วนใหญ่)


สิ่งที่ฉันยังไม่เข้าใจในวันนี้คือเหตุผลที่วิธีการไร้เดียงสาไม่ทำงาน โดยหลักการแล้วสิ่งนี้ควรเป็นผลสืบเนื่องมาจากเอกสารของ Eduardo Giménez แต่ฉันไม่เห็นว่าการหักนั้นแตกที่ใด นี่อาจเป็นข้อ จำกัด ของเอ็นจิ้น Coq มากกว่าแคลคูลัสเป็นต้น
Gilles 'หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.