ก่อนรายการเป็นชนิดของต้นไม้ หากเราแสดงรายการเป็นรายการที่เชื่อมโยงมันเป็นเพียงทรีที่แต่ละโหนดมีลูกหลาน 1 หรือ 0 คน
แยกวิเคราะห์ต้นไม้เป็นเพียงการใช้ประโยชน์ของต้นไม้เป็นโครงสร้างข้อมูล ต้นไม้มีแอปพลิเคชั่นมากมายในสาขาวิทยาศาสตร์คอมพิวเตอร์รวมถึงการเรียงลำดับการนำแผนที่ไปใช้อาเรย์แบบเชื่อมโยง ฯลฯ
โดยทั่วไปรายการต้นไม้ ฯลฯ เป็นโครงสร้างข้อมูลแบบเรียกซ้ำ: แต่ละโหนดมีข้อมูลบางส่วนและอินสแตนซ์อื่นของโครงสร้างข้อมูลเดียวกัน การพับคือการดำเนินการกับโครงสร้างดังกล่าวทั้งหมดที่แปลงโหนดเป็นค่า "bottom up" แบบวนซ้ำ การแฉเป็นกระบวนการย้อนกลับจะแปลงค่าเป็นโหนด "จากบนลงล่าง"
สำหรับโครงสร้างข้อมูลที่กำหนดเราสามารถสร้างฟังก์ชั่นการพับและการแฉกลไกได้
ตัวอย่างเช่นลองทำรายการ (ฉันจะใช้ Haskell เพื่อเป็นตัวอย่างในขณะที่พิมพ์และไวยากรณ์ของมันสะอาดมาก) รายการอาจเป็นจุดสิ้นสุดหรือค่าและ "ส่วนท้าย"
data List a = Nil | Cons a (List a)
สมมติว่าเรากำลังพับรายการ ในแต่ละขั้นตอนเรามีโหนดปัจจุบันที่จะพับและเราได้พับโหนดย่อยซ้ำแล้วซ้ำอีก เราสามารถแสดงสถานะนี้เป็น
data ListF a r = NilF | ConsF a r
โดยที่r
เป็นค่ากลางที่สร้างขึ้นโดยการพับรายการย่อย สิ่งนี้ทำให้เราสามารถแสดงฟังก์ชั่นการพับบนรายการ:
foldList :: (ListF a r -> r) -> List a -> r
foldList f Nil = f NilF
foldList f (Cons x xs) = f (ConsF x (foldList f xs))
เราแปลงList
เข้าไปโดยซ้ำพับมากกว่ารายการย่อยและจากนั้นใช้ฟังก์ชั่นที่กำหนดไว้ในListF
ListF
หากคุณคิดเกี่ยวกับสิ่งนี้นี่เป็นเพียงการนำเสนอมาตรฐานอื่นfoldr
:
foldr :: (a -> r -> r) -> r -> List a -> r
foldr f z = foldList g
where
g NilF = z
g (ConsF x r) = f x r
เราสามารถสร้างunfoldList
ในแบบเดียวกัน:
unfoldList :: (r -> ListF a r) -> r -> List a
unfoldList f r = case f r of
NilF -> Nil
ConsF x r' -> Cons x (unfoldList f r')
อีกครั้งมันเป็นเพียงการเป็นตัวแทนของunfoldr
:
unfoldr :: (r -> Maybe (a, r)) -> r -> [a]
(ขอให้สังเกตว่าMaybe (a, r)
isomorphic ถึงListF a r
)
และเราสามารถสร้างฟังก์ชั่นการตัดไม้ทำลายป่าด้วย:
deforest :: (ListF a r -> r) -> (s -> ListF a s) -> s -> r
deforest f u s = f (map (deforest f u) (u s))
where
map h NilF = NilF
map h (ConsF x r) = ConsF x (h r)
มันแยกกลางList
และผสมฟังก์ชั่นการพับและการคลี่เข้าด้วยกันอย่างง่ายดาย
ขั้นตอนเดียวกันสามารถนำไปใช้กับโครงสร้างข้อมูลแบบเรียกซ้ำ ตัวอย่างเช่นต้นไม้ที่มีโหนดสามารถมี 0, 1, 2 หรือลูกหลานที่มีค่าในโหนด 1- หรือ 0 สาขา:
data Tree a = Bin (Tree a) (Tree a) | Un a (Tree a) | Leaf a
data TreeF a r = BinF r r | UnF a r | LeafF a
treeFold :: (TreeF a r -> r) -> Tree a -> r
treeFold f (Leaf x) = f (LeafF x)
treeFold f (Un x r) = f (UnF x (treeFold f r))
treeFold f (Bin r1 r2) = f (BinF (treeFold f r1) (treeFold f r2))
treeUnfold :: (r -> TreeF a r) -> r -> Tree a
treeUnfold f r = case f r of
LeafF x -> Leaf x
UnF x r -> Un x (treeUnfold f r)
BinF r1 r2 -> Bin (treeUnfold f r1) (treeUnfold f r2)
แน่นอนเราสามารถสร้างdeforestTree
กลไกได้เหมือนเมื่อก่อน
(โดยปกติเราจะแสดงtreeFold
ความสะดวกมากขึ้นเช่น:
treeFold' :: (r -> r -> r) -> (a -> r -> r) -> (a -> r) -> Tree a -> r
)
ฉันจะออกจากรายละเอียดฉันหวังว่ารูปแบบที่ชัดเจน
ดูสิ่งนี้ด้วย: