Ya itu para. Bandingkan dengan katamorfisme, atau 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
Beberapa orang menyebut paramorfisme "rekursi primitif" berbeda dengan katamorfisme ( foldr) menjadi "iterasi".
Di mana foldrdua parameter diberi nilai yang dihitung secara rekursif untuk setiap subobjek rekursif dari data masukan (di sini, itu adalah ekor dari daftar), paraparameter mendapatkan subobjek asli dan nilai yang dihitung secara rekursif darinya.
Contoh fungsi yang diekspresikan dengan baik paraadalah kumpulan daftar yang mencukupi.
suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []
yang seperti itu
suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]
Mungkin masih lebih sederhana
safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing
di mana cabang "kontra" mengabaikan argumen yang dihitung secara rekursif dan hanya mengembalikan ekornya. Dievaluasi dengan malas, komputasi rekursif tidak pernah terjadi dan tail diekstraksi dalam waktu yang konstan.
Anda dapat mendefinisikan foldrdengan paracukup mudah; itu sedikit rumit untuk menentukan paradari foldr, tapi itu pasti mungkin, dan semua orang harus tahu bagaimana hal itu dilakukan!
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)
Trik untuk menentukan paradengan foldradalah merekonstruksi salinan data asli, sehingga kami mendapatkan akses ke salinan ekor di setiap langkah, meskipun kami tidak memiliki akses ke aslinya. Pada akhirnya, sndbuang salinan input dan berikan nilai output saja. Ini tidak terlalu efisien, tetapi jika Anda tertarik pada ekspresifitas semata, paramemberi Anda tidak lebih dari foldr. Jika Anda menggunakan foldrversi yang disandikan ini para, maka safeTailakan membutuhkan waktu linier, menyalin elemen ekor demi elemen.
Jadi, begitulah: paraadalah versi yang lebih nyaman foldryang memberi Anda akses langsung ke ekor daftar serta nilai yang dihitung darinya.
Dalam kasus umum, bekerja dengan tipe data yang dihasilkan sebagai titik tetap rekursif dari sebuah functor
data Fix f = In (f (Fix f))
kamu punya
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)
dan sekali lagi, keduanya dapat didefinisikan satu sama lain, dengan paradidefinisikan dari catatrik "buat salinan" yang sama
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
Sekali lagi, paratidak lebih ekspresif daripada cata, tetapi lebih nyaman jika Anda membutuhkan akses mudah ke substruktur input.
Edit: Saya ingat contoh bagus lainnya.
Pertimbangkan pohon pencarian biner yang diberikan oleh Fix TreeFtempat
data TreeF sub = Leaf | Node sub Integer sub
dan coba definisikan penyisipan untuk pohon penelusuran biner, pertama sebagai a cata, lalu sebagai a para. Anda akan menemukan paraversinya jauh lebih mudah, karena pada setiap node Anda perlu memasukkan satu subpohon tetapi mempertahankan yang lain sebagaimana adanya.
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), methinks.