haskell - 什么是异态?

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

通读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)

但我不明白这一点——我对类型签名或所需的结果没有任何直觉。

什么是拟态,有哪些有用的例子?

<小时/>

是的,我见过these questions ,但它们不直接涵盖同态,仅指向 resources作为引用可能会有帮助,但不能作为学习 Material 。

最佳答案

是的,那就是para。与变形或 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' s 参数获取原始子对象和从中递归计算的值。

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)

再说一次,两者是可以相互定义的,通过相同的“复制”技巧从 cata 定义 para

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

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

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

考虑由 Fix TreeF 给出的二叉搜索树,其中

data TreeF sub = Leaf | Node sub Integer sub

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

关于haskell - 什么是异态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13317242/

相关文章:

haskell - 我可以读取 GHCJS 中的文件吗?

haskell - 括号中的含义是什么 (x :xs) when pattern matching?

java - 生成代表数字的组合

Java无限递归自引用类型

django - 是否可以使用 login_required 修饰 django url 中的 include(...)?

java - C+ +'s accumulate or Groovy' 注入(inject)的 Java 等价物是什么?

haskell - 非尾递归函数在 GHCi 中不会爆炸。为什么?

haskell - 将枚举列表按位表示为 Int

haskell - 使用 ReaderT Maybe 还是 MaybeT Reader?

java - 带递归的 MapReduce