Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 对于未标记的树,“展开”的正确定义是什么?_Haskell_Data Structures_Functional Programming_Unfold - Fatal编程技术网

Haskell 对于未标记的树,“展开”的正确定义是什么?

Haskell 对于未标记的树,“展开”的正确定义是什么?,haskell,data-structures,functional-programming,unfold,Haskell,Data Structures,Functional Programming,Unfold,我一直在思考如何为以下类型实现等价的unfold: data Tree a = Node (Tree a) (Tree a) | Leaf a | Nil 这不是很明显,因为列表的标准展开返回一个值和下一个种子。对于这种数据类型,它没有意义,因为在到达叶节点之前没有“值”。这样,只有返回新种子或使用值停止才真正有意义。我使用这个定义: data Drive s a = Stop | Unit a | Branch s s deriving Show unfold :: (t -> Dr

我一直在思考如何为以下类型实现等价的
unfold

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

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)

虽然这似乎有效,但我不确定这是怎么回事。所以,这就是问题所在:正确的方法是什么?

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

module Unfold where
在这里,我们从定义函子的不动点开始:它是一层
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

此定义专门用于列表中常见的
展开
,或您对树的定义。换句话说:你的定义确实正确。

。。。这看起来是你唯一能做的理智的事,真的吗?我很确定我在这里做了一些愚蠢的事情,但如果它是正确的,我可能会删除这个问题。这个问题很重要,可能对其他人有用。感谢你解释了算法背后的原因,看看我的“驱动器”和“树”数据类型是如何链接的,以及泛型展开是如何简洁,这是非常有启发性的。我想知道为什么Prelude中不是这样定义的?用Fix定义所有递归数据类型会使模式匹配变得非常难看,并且会带来(可能不可忽略的)性能代价。@user2407038为此,如果我们有一个
可强制[a](Fix(ListF a))
实例就太好了,允许我们在普通递归类型和它们的定点同级之间移动。这将具有零运行时成本,并允许两种类型上的模式匹配。(我假设它们具有相同的运行时表示形式,因为
Fix
是一种新类型——不过我可能在这方面错了)。@user2407038模式是rescue的同义词!