มีหลายวิธีที่ดีในการดูที่นี้ สิ่งที่ง่ายที่สุดสำหรับฉันคือคิดถึงความสัมพันธ์ระหว่าง "Inductive" และ "Coinductive definitions"
นิยามอุปนัยของชุดไปเช่นนี้
ชุด "แน็ต" ถูกกำหนดให้เป็นชุดที่เล็กที่สุดเช่น "Zero" ที่อยู่ในแน็ตและถ้า n อยู่ในแน็ต "Succ n" อยู่ในแน็ต
ซึ่งสอดคล้องกับ Ocaml ต่อไปนี้
type nat = Zero | Succ of nat
สิ่งหนึ่งที่ควรทราบเกี่ยวกับคำจำกัดความนี้ก็คือตัวเลข
omega = Succ(omega)
ไม่ใช่สมาชิกของชุดนี้ ทำไม? สมมติว่ามันเป็นตอนนี้พิจารณาชุด N ที่มีองค์ประกอบเดียวกันทั้งหมดกับ Nat ยกเว้นว่ามันจะไม่มีโอเมก้า เห็นได้ชัดว่า Zero อยู่ใน N และถ้า y อยู่ใน N, Succ (y) อยู่ใน N แต่ N นั้นเล็กกว่าแน็ตซึ่งขัดแย้งกัน ดังนั้นโอเมก้าไม่ได้อยู่ในแน็ต
หรืออาจมีประโยชน์มากกว่าสำหรับนักวิทยาศาสตร์คอมพิวเตอร์:
ให้เซต "a" บางชุด "List of a" ถูกกำหนดเป็นชุดที่เล็กที่สุดที่ "Nil" อยู่ในรายการ a และถ้า xs อยู่ในรายการ a และ x อยู่ใน "Cons x xs" อยู่ในรายการของ
ซึ่งสอดคล้องกับสิ่งที่ชอบ
type 'a list = Nil | Cons of 'a * 'a list
คำผ่าตัดที่นี่คือ "เล็กที่สุด" ถ้าเราไม่พูดว่า "เล็กที่สุด" เราคงไม่มีทางบอกได้เลยว่ากลุ่มแน็ตบรรจุกล้วย!
อีกครั้ง
zeros = Cons(Zero,zeros)
ไม่ใช่คำจำกัดความที่ถูกต้องสำหรับรายการของ nats เช่นเดียวกับโอเมก้าไม่ใช่แน็ตที่ถูกต้อง
การกำหนดข้อมูล inductively เช่นนี้ช่วยให้เราสามารถกำหนดฟังก์ชั่นที่ทำงานบนโดยใช้การเรียกซ้ำ
let rec plus a b = match a with
| Zero -> b
| Succ(c) -> let r = plus c b in Succ(r)
จากนั้นเราสามารถพิสูจน์ข้อเท็จจริงเกี่ยวกับสิ่งนี้เช่น "plus a Zero = a" โดยใช้การเหนี่ยวนำ (โดยเฉพาะการเหนี่ยวนำเชิงโครงสร้าง)
หลักฐานของเราดำเนินการโดยการเหนี่ยวนำโครงสร้างใน
สำหรับกรณีพื้นฐานให้เป็นศูนย์ เพื่อให้เรารู้plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)
plus Zero Zero = Zero
อนุญาตa
เป็น nat plus a Zero = a
สมมติสมมติฐานว่า ตอนนี้เราแสดงให้เห็นว่าplus (Succ(a)) Zero = Succ(a)
สิ่งนี้ชัดเจนตั้งแต่plus (Succ(a)) Zero = match a with |Zero -> Zero | Succ(a) -> let r = plus a Zero in Succ(r) = let r = a in Succ(r) = Succ(a)
ดังนั้นโดยอุปนัยplus a Zero = a
สำหรับทุกคนa
ในนัท
แน่นอนเราสามารถพิสูจน์สิ่งที่น่าสนใจมากขึ้น แต่นี่เป็นความคิดทั่วไป
จนถึงตอนนี้เราได้จัดการกับข้อมูลที่กำหนดแบบเหนี่ยวนำซึ่งเราได้รับโดยปล่อยให้เป็นชุด "ที่เล็กที่สุด" ดังนั้นตอนนี้เราต้องการที่จะทำงานร่วมกับcodata ที่กำหนดไว้โดย coinductivly ที่เราได้รับโดยให้มันเป็นชุดที่ใหญ่ที่สุด
ดังนั้น
ปล่อยให้เป็นเซต ชุด "สตรีมของ" ถูกกำหนดให้เป็นชุดที่ใหญ่ที่สุดเช่นว่าสำหรับแต่ละ x ในสตรีมของ a, x ประกอบด้วยคู่ที่สั่งซื้อ (หัวหาง) ซึ่งหัวอยู่ในและท้ายอยู่ในสตรีมของ
ใน Haskell เราจะแสดงสิ่งนี้เป็น
data Stream a = Stream a (Stream a) --"data" not "newtype"
ที่จริงแล้วใน Haskell เราใช้รายการในตัวตามปกติซึ่งอาจเป็นคู่ที่สั่งหรือรายการที่ว่างเปล่า
data [a] = [] | a:[a]
Banana ไม่ใช่สมาชิกของประเภทนี้เช่นกันเนื่องจากไม่ใช่คู่ที่สั่งซื้อหรือรายการว่าง แต่ตอนนี้เราสามารถพูดได้
ones = 1:ones
และนี่คือนิยามที่ถูกต้องสมบูรณ์แบบ ยิ่งไปกว่านั้นเราสามารถทำการสอบถามซ้ำซ้อนกับข้อมูลร่วมนี้ได้ ที่จริงแล้วมันเป็นไปได้ที่ฟังก์ชั่นจะเป็นแบบร่วมซ้ำและแบบเรียกซ้ำ ในขณะที่การเรียกซ้ำถูกกำหนดโดยฟังก์ชั่นที่มีโดเมนที่ประกอบด้วยข้อมูลการสอบถามซ้ำก็หมายความว่ามันมีโดเมนร่วม (เรียกอีกอย่างว่าช่วง) ที่เป็นข้อมูลร่วม การเรียกซ้ำแบบดั้งเดิมหมายถึง "การเรียกตนเอง" กับข้อมูลที่เล็กกว่าเสมอจนกว่าจะถึงข้อมูลที่เล็กที่สุด การเรียกซ้ำตัวเองแบบดั้งเดิมมักจะ "เรียกตัวเองว่า" กับข้อมูลที่มากกว่าหรือเท่ากับสิ่งที่คุณเคยมีมาก่อน
ones = 1:ones
เป็นแบบเรียกซ้ำซ้อน ในขณะที่ฟังก์ชั่นmap
(เช่น "foreach" ในภาษาบังคับ) เป็นทั้งแบบเรียกซ้ำ (แบบเรียงลำดับ) และแบบเรียกซ้ำแบบดั้งเดิม
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = (f x):map f xs
ไปสำหรับฟังก์ชั่นzipWith
ที่ใช้ฟังก์ชั่นและคู่ของรายการและรวมเข้าด้วยกันโดยใช้ฟังก์ชั่นนั้น
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = (f a b):zipWith f as bs
zipWith _ _ _ = [] --base case
ตัวอย่างคลาสสิกของภาษาที่ใช้งานได้คือลำดับฟีโบนักชี
fib 0 = 0
fib 1 = 1
fib n = (fib (n-1)) + (fib (n-2))
ซึ่งเรียกซ้ำแบบดั้งเดิม แต่สามารถแสดงได้อย่างสง่างามมากขึ้นเป็นรายการที่ไม่มีที่สิ้นสุด
fibs = 0:1:zipWith (+) fibs (tail fibs)
fib' n = fibs !! n --the !! is haskell syntax for index at
ตัวอย่างที่น่าสนใจของการเหนี่ยวนำ / การทำเหรียญคือการพิสูจน์ว่าคำจำกัดความทั้งสองนี้คำนวณสิ่งเดียวกัน นี่เป็นแบบฝึกหัดสำหรับผู้อ่าน