什么是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
版本更容易,因为在每一个节点你都需要插入一个子树,但保留原来的一个。