【问题标题】:What are paramorphisms?什么是超态?
【发布时间】:2012-10-30 07:32:22
【问题描述】:

通读this classic paper,我陷入了同构问题。不幸的是,该部分很薄,维基百科页面没有说明任何内容。

我的 Haskell 翻译是:

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

但我不明白——我对类型签名或期望的结果没有任何直觉。

什么是变形,有哪些有用的例子?


是的,我见过thesequestions,但它们并没有直接涉及到变形,只指向resources,这可能有助于作为参考,但不能作为学习材料。

【问题讨论】:

  • para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs),我想。
  • 根据this wiki page,paramorphisms“对归纳数据类型进行模型原始递归”。这意味着什么/帮助?
  • 我在对其中一个问题的评论中指出的 Jeremy Gibbons 的“Fission”论文是非常有用的学习材料。 cs.ox.ac.uk/jeremy.gibbons/publications/fission.pdf 它可以非常清晰地处理多种递归模式。
  • 丹尼尔重写can be simplified aspara f base xs = foldr g base (init $ tails xs) where g (x:xs) = f x xs。这让人想起Common Lisp's maplist

标签: haskell recursion functional-programming higher-order-functions


【解决方案1】:

是的,那是para。比较一下catamorphism,或者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

有些人将变态称为“原始递归”,而变态 (foldr) 称为“迭代”。

其中foldr的两个参数为输入数据的每个递归子对象(这里是列表的尾部)给出一个递归计算的值,para的参数获取原始子对象和值从中递归计算。

一个用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”分支忽略其递归计算的参数并返回尾部。懒惰地评估,递归计算永远不会发生,并且在恒定时间内提取尾部。

您可以很容易地使用para 定义foldr;从foldr 定义para 有点棘手,但肯定是有可能的,而且每个人都应该知道它是怎么做到的!

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)

foldr 定义para 的技巧是重建原始数据的副本,以便我们在每一步都可以访问尾部的副本,即使我们有无法访问原件。最后,snd 丢弃输入的副本,只给出输出值。这不是很有效,但如果你对纯粹的表现力感兴趣,para 给你的只是foldr。如果您使用parafoldr 编码版本,那么safeTail 毕竟将花费线性时间,逐个元素地复制尾部。

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

在一般情况下,使用作为仿函数的递归固定点生成的数据类型

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

你有

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)

再一次,这两者是可以相互定义的,paracata 定义,由相同的“复制”技巧

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

同样,para 并不比 cata 更具表现力,但如果您需要轻松访问输入的子结构,则更方便。

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

考虑由Fix TreeF where 给出的二叉搜索树

data TreeF sub = Leaf | Node sub Integer sub

并尝试为二叉搜索树定义插入,首先是cata,然后是para。您会发现para 版本更容易,因为您需要在每个节点处插入一个子树,但保留另一个子树。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-01
    • 2022-01-19
    • 2012-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多