9

我一直在考虑如何实现unfold以下类型的等价物:

data Tree a = Node (Tree a) (Tree a) | Leaf a | Nil

这不是很明显,因为unfold列表的标准返回一个值和下一个种子。对于这种数据类型,它没有意义,因为在到达叶节点之前没有“值”。这样,只有返回新种子或以值停止才真正有意义。我正在使用这个定义:

data Drive s a = Stop | Unit a | Branch s s deriving Show

unfold :: (t -> Drive t a) -> t -> Tree a
unfold fn x = case fn x of
    Branch a b -> Node (unfold fn a) (unfold fn b)
    Unit a     -> Leaf a
    Stop       -> Nil

main = print $ unfold go 5 where
    go 0 = Stop
    go 1 = Unit 1
    go n = Branch (n - 1) (n - 2)

虽然这似乎可行,但我不确定这是否应该是这样。所以,这就是问题:正确的方法是什么?

4

1 回答 1

9

如果您将数据类型视为函子的固定点,那么您可以看到您的定义是列表案例的合理概括。

module Unfold where

在这里,我们首先定义仿函数的固定点f:它是一层,f后面跟着更多的固定点:

newtype Fix f = InFix { outFix :: f (Fix f) }

为了让事情稍微清楚一点,这里是对应于列表和树的函子的定义。它们与数据类型的形状基本相同,只是我们用一个额外的参数替换了递归调用。换句话说,它们描述了一层列表/树的样子,并且对可能的子结构是通用的r

data ListF a r = LNil | LCons a r
data TreeF a r = TNil | TLeaf a | TBranch r r

列表和树分别是 ListF 和 TreeF 的固定点:

type List a = Fix (ListF a)
type Tree a = Fix (TreeF a)

无论如何,让您现在对这个固定点业务有了更好的直觉,我们可以看到有一种通用的方法来定义unfold这些函数。

给定一个原始种子以及一个获取种子并构建递归结构是新种子的一层的函数,我们可以构建一个完整的结构:f

unfoldFix :: Functor f => (s -> f s) -> s -> Fix f
unfoldFix node = go
  where go = InFix . fmap go . node

此定义专门针对通常unfold的列表或您对树的定义。换句话说:您的定义确实是正确的。

于 2015-02-22T00:16:19.343 回答