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 foldr
bireyin 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 foldr
kullanarak para
oldukça kolay; o tanımlamak için biraz daha yanıltıcıdır para
gelen 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 para
ile foldr
bir 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, snd
girdinin kopyasını atar ve sadece çıktı değerini verir. Çok verimli değil, ancak tamamen ifade ile ilgileniyorsanız, para
size bundan fazlasını vermez foldr
. Bu foldr
kodlanmış sürümünü kullanırsanız para
, safeTail
her ş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 para
daha 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 para
tanımlanarak karşılıklı olarak tanımlanabilircata
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
Yine, para
daha 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 TreeF
Nereye 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
. para
Her 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.