Evet, bu para. Katamorfizm ile karşılaştırın veya 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
Bazı insanlar, katamorfizmlerin ( foldr) "yineleme" olmasının aksine, paramorfizmaları "ilkel özyineleme" olarak adlandırırlar .
Nerede foldrbireyin iki parametre (listenin kuyruğu burada,) giriş verilerinin her özyinelemeli altnesnesi için yinelemeli hesaplanan değer verilir, para'in parametreleri orijinal subobject ve ondan yinelemeli hesaplanan değeri hem olsun.
Güzel bir şekilde ifade edilen örnek bir işlev para, bir listenin uygun yeterliklerinin toplanmasıdır.
suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []
Böylece
suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]
Muhtemelen daha basit, yine de
safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing
burada "eksiler" dalı, yinelemeli olarak hesaplanan argümanını görmezden gelir ve sadece kuyruğu geri verir. Tembel olarak değerlendirildiğinde, özyinelemeli hesaplama asla gerçekleşmez ve kuyruk sabit zamanda çıkarılır.
Sen tanımlayabilirsiniz foldrkullanarak paraoldukça kolay; o tanımlamak için biraz daha yanıltıcıdır paragelen foldr, ama kesinlikle mümkündür ve herkes nasıl yapıldığını bilmeli!
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)
Tanımlamanın hile paraile foldrbir yeniden inşa etmektir kopyasını biz orijinal erişimi olmasına rağmen, her adımda kuyruk bir kopyasını erişebilmek böylece, orijinal verilerin. Sonunda, sndgirdinin kopyasını atar ve sadece çıktı değerini verir. Çok verimli değil, ancak tamamen ifade ile ilgileniyorsanız, parasize bundan fazlasını vermez foldr. Bu foldrkodlanmış sürümünü kullanırsanız para, safeTailher şeyden önce kuyruk elemanını elemanlara kopyalayarak doğrusal zaman alacaktır.
İşte bu kadar: listenin kuyruğuna ve ondan hesaplanan değere anında erişim sağlayan paradaha kullanışlı bir sürümüdür foldr.
Genel durumda, bir functor'un özyinelemeli sabit noktası olarak oluşturulan bir veri türü ile çalışma
data Fix f = In (f (Fix f))
var
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)
ve yine, ikisi de aynı "kopya yap" numarasıyla paratanımlanarak karşılıklı olarak tanımlanabilircata
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
Yine, paradaha anlamlı değildir cata, ancak girdinin alt yapılarına kolay erişime ihtiyacınız varsa daha kullanışlıdır.
Düzenleme: Başka bir güzel örnek hatırladım.
Fix TreeFNereye göre verilen ikili arama ağaçlarını düşünün
data TreeF sub = Leaf | Node sub Integer sub
ve ikili arama ağaçları için eklemeyi önce a cata, sonra a olarak tanımlamayı deneyin para. paraHer düğümde bir alt ağaca eklemeniz, ancak diğerini olduğu gibi korumanız gerekeceğinden sürümü çok daha kolay bulacaksınız .
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), benim için.