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.