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 foldr
den beiden Parametern für jedes rekursive Unterobjekt der Eingabedaten ein rekursiv berechneter Wert zugewiesen wird (hier ist dies das Ende der Liste), erhalten para
die Parameter sowohl das ursprüngliche Unterobjekt als auch den daraus rekursiv berechneten Wert.
Eine Beispielfunktion, die sich gut ausdrückt, para
ist 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 , foldr
mit para
ganz leicht; es ist ein wenig schwieriger zu definieren , para
aus 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 para
mit foldr
ist 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 snd
die Kopie der Eingabe verworfen und nur der Ausgabewert angegeben. Es ist nicht sehr effizient, aber wenn Sie an Ausdruckskraft interessiert sind, erhalten para
Sie nicht mehr als foldr
. Wenn Sie diese foldr
-kodierte Version von verwenden para
, dauert safeTail
es schließlich linear, das Ende Element für Element zu kopieren.
Das war's also: Es para
ist eine bequemere Version, mit foldr
der 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 para
definierten cata
mit dem gleichen „eine Kopie“ Trick
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
Auch dies para
ist 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 TreeF
where 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 para
Version 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.