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 foldr
dua parameter diberi nilai yang dihitung secara rekursif untuk setiap subobjek rekursif dari data masukan (di sini, itu adalah ekor dari daftar), para
parameter mendapatkan subobjek asli dan nilai yang dihitung secara rekursif darinya.
Contoh fungsi yang diekspresikan dengan baik para
adalah 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 foldr
dengan para
cukup mudah; itu sedikit rumit untuk menentukan para
dari 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 para
dengan foldr
adalah merekonstruksi salinan data asli, sehingga kami mendapatkan akses ke salinan ekor di setiap langkah, meskipun kami tidak memiliki akses ke aslinya. Pada akhirnya, snd
buang salinan input dan berikan nilai output saja. Ini tidak terlalu efisien, tetapi jika Anda tertarik pada ekspresifitas semata, para
memberi Anda tidak lebih dari foldr
. Jika Anda menggunakan foldr
versi yang disandikan ini para
, maka safeTail
akan membutuhkan waktu linier, menyalin elemen ekor demi elemen.
Jadi, begitulah: para
adalah versi yang lebih nyaman foldr
yang 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 para
didefinisikan dari cata
trik "buat salinan" yang sama
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
Sekali lagi, para
tidak 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 TreeF
tempat
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 para
versinya 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.