paraใช่ว่า เปรียบเทียบกับ catamorphism หรือfoldr:
para :: (a -> [a] -> b -> b) -> b -> [a] -> b
foldr :: (a -> b -> b) -> b -> [a] -> b
para c n (x : xs) = c x xs (para c n xs)
foldr c n (x : xs) = c x (foldr c n xs)
para c n [] = n
foldr c n [] = n
บางคนเรียกพารามอร์ฟิสม์ว่า "การเรียกซ้ำแบบดั้งเดิม" ตรงกันข้ามกับ catamorphisms ( foldr) คือ "การวนซ้ำ"
โดยที่foldrพารามิเตอร์สองตัวจะได้รับค่าที่คำนวณซ้ำสำหรับแต่ละอ็อบเจ็กต์ย่อยแบบวนซ้ำของข้อมูลอินพุต (ที่นี่คือส่วนท้ายของรายการ) paraพารามิเตอร์จะได้รับทั้งวัตถุย่อยดั้งเดิมและค่าที่คำนวณซ้ำจากมัน
ฟังก์ชันตัวอย่างที่แสดงออกมาอย่างดีparaคือการรวบรวมความพอเพียงของรายการ
suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []
ดังนั้น
suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]
อาจจะง่ายกว่านั้นก็คือ
safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing
ซึ่งสาขา "ข้อเสีย" จะไม่สนใจอาร์กิวเมนต์ที่คำนวณซ้ำและเพียงแค่ให้หางกลับ จากการประเมินอย่างเกียจคร้านการคำนวณแบบวนซ้ำไม่เคยเกิดขึ้นและหางจะถูกดึงออกมาในเวลาคงที่
คุณสามารถกำหนดfoldrโดยใช้paraค่อนข้างง่าย มันเป็นเพียงเล็กน้อย trickier เพื่อกำหนดparaจากfoldrแต่มันก็เป็นไปได้อย่างแน่นอนและทุกคนควรจะรู้ว่ามันทำ!
foldr c n = para (\ x xs t -> c x t) n
para c n = snd . foldr (\ x (xs, t) -> (x : xs, c x xs t)) ([], n)
เคล็ดลับที่จะกำหนดparaด้วยfoldrคือการบูรณะสำเนาของข้อมูลเดิมเพื่อที่เราจะได้รับการเข้าถึงสำเนาของหางในแต่ละขั้นตอนแม้ว่าเราจะมีการเข้าถึงเดิมไม่มี ในตอนท้ายให้sndละทิ้งสำเนาของอินพุตและให้เพียงค่าเอาต์พุต มันไม่ได้มีประสิทธิภาพมาก แต่ถ้าคุณสนใจใน expressivity แท้จริงจะช่วยให้คุณไม่เกินpara foldrถ้าคุณใช้นี้foldrรุ่น -encoded ของparaแล้วsafeTailจะใช้เวลาในการเชิงเส้นหลังจากทั้งหมดคัดลอกองค์ประกอบหางโดยองค์ประกอบ
นั่นparaแหล่ะ: เป็นเวอร์ชันที่สะดวกกว่าfoldrซึ่งช่วยให้คุณเข้าถึงส่วนท้ายของรายการได้ทันทีรวมทั้งค่าที่คำนวณจากมัน
ในกรณีทั่วไปการทำงานกับประเภทข้อมูลที่สร้างขึ้นเป็นฟิกซ์พอยต์แบบเรียกซ้ำของ functor
data Fix f = In (f (Fix f))
คุณมี
cata :: Functor f => (f t -> t) -> Fix f -> t
para :: Functor f => (f (Fix f, t) -> t) -> Fix f -> t
cata phi (In ff) = phi (fmap (cata phi) ff)
para psi (In ff) = psi (fmap keepCopy ff) where
keepCopy x = (x, para psi x)
และอีกครั้งทั้งสองสามารถกำหนดร่วมกันได้โดยparaกำหนดจากcataเคล็ดลับ "ทำสำเนา" เดียวกัน
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
อีกครั้งparaไม่ได้แสดงออกมากไปกว่าcataแต่สะดวกกว่าหากคุณต้องการเข้าถึงโครงสร้างย่อยของอินพุตได้ง่าย
แก้ไข:ฉันจำได้อีกตัวอย่างที่ดี
พิจารณาต้นไม้ค้นหาแบบไบนารีที่กำหนดโดยFix TreeFที่ไหน
data TreeF sub = Leaf | Node sub Integer sub
และลองกำหนดการแทรกสำหรับต้นไม้ค้นหาแบบไบนารีก่อนเป็น a cataแล้วจึงเป็นpara. คุณจะพบว่าparaเวอร์ชันนั้นง่ายกว่ามากเนื่องจากในแต่ละโหนดคุณจะต้องแทรกในทรีย่อยหนึ่ง แต่รักษาอีกรายการไว้เหมือนเดิม
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), methinks.