什么是paramorphisms?

通过阅读这篇经典论文 ,我被困在paramorphisms。 不幸的是,该部分是非常薄,维基百科页面没有说什么。

我的Haskell翻译是:

para :: (a -> [a] -> b -> b) -> b -> [a] -> b para f base = h where h [] = base h (x:xs) = fx xs (h xs) 

但是我并不这么认为 – 我没有任何types签名或期望结果的直觉。

什么是paramorphism,什么是一些有用的例子在行动?


是的,我已经看到了这些 问题 ,但是它们并没有直接涵盖副变形,只是指向可能有助于引用的资源 ,而不是作为学习材料。

是的,这是para 。 比较变形,或者foldr

 para :: (a -> [a] -> b -> b) -> b -> [a] -> b foldr :: (a -> b -> b) -> b -> [a] -> b para cn (x : xs) = cx xs (para cn xs) foldr cn (x : xs) = cx (foldr cn xs) para cn [] = n foldr cn [] = n 

有些人把变形( foldr )称为“迭代(iteration)”,称之为“原始recursion”(paramorphisms)。

foldr的两个参数中,对于input数据的每个recursion子对象(这里是列表的尾部)给出recursion计算的值, para的参数同时得到原始的子对象和由它recursion计算的值。

一个很好地用para表示的函数就是列表的正确组合。

 suff :: [x] -> [[x]] suff = para (\ x xs suffxs -> xs : suffxs) [] 

以便

 suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""] 

可能更简单

 safeTail :: [x] -> Maybe [x] safeTail = para (\ _ xs _ -> Just xs) Nothing 

其中“cons”分支忽略了它的recursion计算的参数,并且只是给出尾部。 懒洋洋地评价,recursion计算永远不会发生,并且尾部在不变的时间被提取。

你可以很容易地使用para定义foldr ; 从foldr定义para有点麻烦,但是这当然是可能的,每个人都应该知道它是如何完成的!

 foldr cn = para (\ x xs t -> cxt) n para cn = snd . foldr (\ x (xs, t) -> (x : xs, cx xs t)) ([], n) 

使用foldr定义para的技巧是重build原始数据的副本 ,这样即使我们无法访问原始数据,我们也可以在每个步骤访问尾部副本。 最后, snd放弃input的副本,只给出输出值。 这不是很有效率,但是如果你对纯粹的expression感兴趣,那么para就不会给你带来什么了。 如果你使用这个foldr编码版本的para ,那么safeTail将花费线性时间,通过元素复制tail元素。

所以,就是这样: para是一个更方便的foldr版本,它使您可以立即访问列表尾部以及从中计算出的值。

在一般情况下,使用作为函数的recursion定点生成的数据types

 data Fix f = In (f (Fix f)) 

你有

 cata :: Functor f => (ft -> 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) 

再次,这两者是可以相互定义的,同样由cata通过相同的“复制”技巧来界定

 para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt)) 

para再次比cata没有performancecata ,但是如果你需要方便地访问input的子结构,则更方便。

编辑:我记得另一个很好的例子。

考虑由Fix TreeF给出的二叉search树在哪里

 data TreeF sub = Leaf | Node sub Integer sub 

并尝试定义插入二叉search树,首先作为一个cata ,然后作为一个para 。 你会发现para版本更容易,因为在每一个节点你都需要插入一个子树,但保留原来的一个。