Ja, das ist para. Vergleiche mit Katamorphose oder 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
Einige Leute nennen Paramorphismen "primitive Rekursion", während Katamorphismen ( foldr) "Iteration" sind.
Wenn foldrden beiden Parametern für jedes rekursive Unterobjekt der Eingabedaten ein rekursiv berechneter Wert zugewiesen wird (hier ist dies das Ende der Liste), erhalten paradie Parameter sowohl das ursprüngliche Unterobjekt als auch den daraus rekursiv berechneten Wert.
Eine Beispielfunktion, die sich gut ausdrückt, paraist die Sammlung der richtigen Suffizien einer Liste.
suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []
damit
suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]
Möglicherweise ist es noch einfacher
safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing
in dem der "cons" -Zweig sein rekursiv berechnetes Argument ignoriert und nur den Schwanz zurückgibt. Faul ausgewertet findet die rekursive Berechnung nie statt und der Schwanz wird in konstanter Zeit extrahiert.
Sie können festlegen , foldrmit paraganz leicht; es ist ein wenig schwieriger zu definieren , paraaus foldr, aber es ist durchaus möglich, und jeder sollte wissen , wie es geht!
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)
Der Trick , um die Definition paramit foldrist eine rekonstruieren Kopie der Originaldaten, so dass wir bei jedem Schritt Zugriff auf eine Kopie des Schwanzes gewinnen, auch wenn wir keinen Zugriff auf das Original hatten. Am Ende wird snddie Kopie der Eingabe verworfen und nur der Ausgabewert angegeben. Es ist nicht sehr effizient, aber wenn Sie an Ausdruckskraft interessiert sind, erhalten paraSie nicht mehr als foldr. Wenn Sie diese foldr-kodierte Version von verwenden para, dauert safeTailes schließlich linear, das Ende Element für Element zu kopieren.
Das war's also: Es paraist eine bequemere Version, mit foldrder Sie sofort auf das Ende der Liste sowie auf den daraus berechneten Wert zugreifen können.
Im allgemeinen Fall wird mit einem Datentyp gearbeitet, der als rekursiver Fixpunkt eines Funktors generiert wurde
data Fix f = In (f (Fix f))
du hast
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)
und wieder sind die beiden zueinander definierbar, mit paradefinierten catamit dem gleichen „eine Kopie“ Trick
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
Auch dies paraist nicht aussagekräftiger als cata, aber bequemer, wenn Sie einen einfachen Zugriff auf Unterstrukturen der Eingabe benötigen.
Edit: Ich erinnerte mich an ein anderes schönes Beispiel.
Betrachten Sie binäre Suchbäume, die durch Fix TreeFwhere angegeben werden
data TreeF sub = Leaf | Node sub Integer sub
und versuchen Sie, die Einfügung für binäre Suchbäume zuerst als cata, dann als zu definieren para. Sie werden die paraVersion viel einfacher finden, da Sie an jedem Knoten einen Teilbaum einfügen müssen, den anderen aber beibehalten müssen.
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), denkt nach.