Paramorphisms คืออะไร?


95

เมื่ออ่านบทความคลาสสิกนี้ฉันติดอยู่กับพารามอร์ฟิสม์ น่าเสียดายที่ส่วนนี้ค่อนข้างบางและหน้า Wikipedia ไม่ได้พูดอะไรเลย

การแปล Haskell ของฉันคือ:

para :: (a -> [a] -> b -> b) -> b -> [a] -> b
para f base = h
  where
    h []       =   base
    h (x:xs)   =   f x xs (h xs)

แต่ฉันไม่เอะใจนั่น - ฉันไม่มีสัญชาตญาณใด ๆ สำหรับลายเซ็นประเภทหรือผลลัพธ์ที่ต้องการ

พารามอร์ฟิสซึมคืออะไรและมีตัวอย่างใดบ้างที่เป็นประโยชน์ในการดำเนินการ


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


1
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), methinks.
Daniel Fischer

ตามหน้าวิกินี้พารามอร์ฟิสม์ "การเรียกซ้ำแบบจำลองแบบดั้งเดิมเหนือประเภทข้อมูลอุปนัย" หมายความว่าอะไร / ช่วย?
huon

4
กระดาษ "Fission" ของ Jeremy Gibbons ที่ฉันชี้ไปในความคิดเห็นของคำถามเหล่านี้เป็นสื่อการเรียนรู้ที่มีประโยชน์มาก cs.ox.ac.uk/jeremy.gibbons/publications/fission.pdfมันทำงานผ่านรูปแบบการเรียกซ้ำจำนวนมากอย่างชัดเจน
stephen tetley

1
การเขียนซ้ำของ Daniel สามารถทำให้ง่ายขึ้นเป็น para f base xs = foldr g base (init $ tails xs) where g (x:xs) = f x xs . นี้จะแจ้งเตือนของCommon maplistเสียงกระเพื่อมของ
Will Ness

คำตอบ:


109

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เวอร์ชันนั้นง่ายกว่ามากเนื่องจากในแต่ละโหนดคุณจะต้องแทรกในทรีย่อยหนึ่ง แต่รักษาอีกรายการไว้เหมือนเดิม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.