Pertama, daftar adalah sejenis pohon. Jika kami mewakili daftar sebagai daftar tertaut , itu hanya pohon yang setiap simpulnya memiliki keturunan 1 atau 0.
Pohon parse hanyalah pemanfaatan pohon sebagai struktur data. Pohon memiliki banyak banyak aplikasi dalam ilmu komputer, termasuk menyortir, mengimplementasikan peta, array asosiatif, dll.
Secara umum, daftar, pohon dll adalah struktur data rekursif: Setiap node berisi beberapa informasi dan contoh lain dari struktur data yang sama. Lipat adalah operasi atas semua struktur seperti itu yang secara rekursif mengubah node menjadi nilai "bottom up". Berlangsung adalah proses sebaliknya, itu mengkonversi nilai menjadi node "top down".
Untuk struktur data yang diberikan, kita dapat secara mekanis membangun fungsi lipatan dan lipatannya.
Sebagai contoh, mari kita ambil daftar. (Saya akan menggunakan Haskell untuk contoh-contoh saat diketik dan sintaksnya sangat bersih.) Daftar bisa berupa akhir atau nilai dan "ekor".
data List a = Nil | Cons a (List a)
Sekarang mari kita bayangkan kita melipat daftar. Pada setiap langkah, kami memiliki simpul saat ini untuk dilipat dan kami telah melipat sub-node rekursifnya. Kita dapat mewakili negara ini sebagai
data ListF a r = NilF | ConsF a r
di mana r
nilai menengah dibangun dengan melipat sublist. Ini memungkinkan kami untuk mengekspresikan fungsi lipat dari daftar:
foldList :: (ListF a r -> r) -> List a -> r
foldList f Nil = f NilF
foldList f (Cons x xs) = f (ConsF x (foldList f xs))
Kami mengkonversi List
menjadi ListF
dengan secara rekursif melipat lebih dari sublist dan kemudian menggunakan fungsi yang ditentukan ListF
. Jika Anda memikirkannya, ini hanyalah representasi standar lain 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
Kita dapat membangun unfoldList
dengan cara yang sama:
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')
Sekali lagi, ini hanyalah representasi lain dari unfoldr
:
unfoldr :: (r -> Maybe (a, r)) -> r -> [a]
(Perhatikan itu Maybe (a, r)
isomorfik untuk ListF a r
.)
Dan kita dapat membangun fungsi deforestasi juga:
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)
Ini hanya meninggalkan fungsi antara List
dan sekering fungsi lipat dan terbuka bersama.
Prosedur yang sama dapat diterapkan pada struktur data rekursif apa pun. Misalnya, pohon yang simpulnya dapat memiliki 0, 1, 2 atau turunan dengan nilai pada simpul bercabang 1- atau 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)
Tentu saja, kita dapat membuat deforestTree
secara mekanis seperti sebelumnya.
(Biasanya, kami akan mengekspresikan treeFold
lebih nyaman sebagai:
treeFold' :: (r -> r -> r) -> (a -> r -> r) -> (a -> r) -> Tree a -> r
)
Saya akan meninggalkan detailnya, saya harap polanya jelas.
Lihat juga: