将值向上传播到Haskell中的Data.Tree中
使用将值向上传播到Haskell中的Data.Tree中,haskell,tree,functional-programming,Haskell,Tree,Functional Programming,使用Data.Tree我可以定义如下树: mkTree :: T.Tree Double mkTree = T.Node 0 [ T.Node 4 [] , T.Node 0 [T.Node 5 [], T.Node 4 []] , T.Node 0 [T.Node 2 [], T.Node 1 []] ] 这将转移到: 0.0 | +- 4.0 | +- 0.0 | | | +-
Data.Tree
我可以定义如下树:
mkTree :: T.Tree Double
mkTree = T.Node 0 [ T.Node 4 []
, T.Node 0 [T.Node 5 [], T.Node 4 []]
, T.Node 0 [T.Node 2 [], T.Node 1 []]
]
这将转移到:
0.0
|
+- 4.0
|
+- 0.0
| |
| +- 5.0
| |
| `- 4.0
|
`- 0.0
|
+- 2.0
|
`- 1.0
现在我想转换树,使每个T.Node
现在包含其子节点的和(或某些其他函数):
16.0
|
+- 4.0
|
+- 9.0
| |
| +- 5.0
| |
| `- 4.0
|
`- 3.0
|
+- 2.0
|
`- 1.0
问题是我无法使用fmap
访问节点的子节点。到目前为止,我掌握了以下功能:
propagate :: Num a => T.Tree a -> T.Tree a
propagate (T.Node x []) = T.Node x []
propagate (T.Node _ ts) = T.Node (sum $ map gather ts) (map propagate ts)
gather :: Num a => T.Tree a -> a
gather (T.Node n []) = n
gather (T.Node _ ts) = sum $ map gather ts
但这似乎太复杂了,特别是如果我用另一个函数替换
sum
。也许有更好的方法可以使用可折叠的
或可遍历的
?对每个节点进行操作,包括所有子树(节点标签),然后可以使用一个有用的函数
subtrees :: Tree a -> Tree (Tree a)
subtrees n@(Node _ xs) = Node n (map subtrees xs)
现在,您可以将任意树函数应用于任意树
sumSubtree :: Num a => Tree a -> Tree a
sumSubtree = fmap sum . subtrees
达到了预期的效果
正如Daniel所说,这个sumSubtree
是低效的,因为从叶子到根的和具有最佳的子结构
但是,不存在唯一的解决方案,请查看下一个折叠版本
foldTree :: (a → Forest b → b) → Tree a → Tree b
foldTree f (Node x xs) = Node (f x xs') xs'
where xs' = foldTree f ↥ xs
仅当f
不需要根分支和以前的分支来计算某个分支值(例如求和问题)时,now才是最优的。但是(例如)如果在每个节点上存储了一些密钥,那么这个sum实现也将是低效的
(使用前面的折叠,求和问题可以写成
foldTree(λx xs)→ ∑(x:map rootLabel xs))
)对于每个节点,使用所有子树(包括节点标签),可以使用一个有用的函数
subtrees :: Tree a -> Tree (Tree a)
subtrees n@(Node _ xs) = Node n (map subtrees xs)
现在,您可以将任意树函数应用于任意树
sumSubtree :: Num a => Tree a -> Tree a
sumSubtree = fmap sum . subtrees
达到了预期的效果
正如Daniel所说,这个sumSubtree
是低效的,因为从叶子到根的和具有最佳的子结构
但是,不存在唯一的解决方案,请查看下一个折叠版本
foldTree :: (a → Forest b → b) → Tree a → Tree b
foldTree f (Node x xs) = Node (f x xs') xs'
where xs' = foldTree f ↥ xs
仅当f
不需要根分支和以前的分支来计算某个分支值(例如求和问题)时,now才是最优的。但是(例如)如果在每个节点上存储了一些密钥,那么这个sum实现也将是低效的
(使用前面的折叠,求和问题可以写成
foldTree(λx xs)→ ∑(x:map rootLabel xs))
)我不认为可折叠的暴露了足够多的树的结构来做你想做的事情<代码>可遍历的
可能会,但要正确处理似乎相对比较棘手;我想我更喜欢实现这样的递归模式:
foldTree :: (a -> [b] -> b) -> Tree a -> b
foldTree f = go where
go (Node value children) = f value (map go children)
然后,您可以按如下方式实现求和操作
sums :: Num a => Tree a -> Tree a
sums = foldTree (\val children -> Node (sum (val:map rootLabel children)) children)
或者甚至用sconcat
和(:)
代替sum
和(:)将Num
推广到半群
,我认为可折叠
暴露的树的结构不足以满足您的需要<代码>可遍历的
可能会,但要正确处理似乎相对比较棘手;我想我更喜欢实现这样的递归模式:
foldTree :: (a -> [b] -> b) -> Tree a -> b
foldTree f = go where
go (Node value children) = f value (map go children)
然后,您可以按如下方式实现求和操作
sums :: Num a => Tree a -> Tree a
sums = foldTree (\val children -> Node (sum (val:map rootLabel children)) children)
或者甚至用sconcat
和(:)
来代替sum
和(:),将Num
推广到半群
。但这效率极低,因为它经常会重新计算深子树的和。@DanielWagner我认为你关于f
的假设是错误的,“问题是我无法访问节点的子节点”,求和问题是一个示例,而不是一般情况。您可以优化尾部计算(求和情况)但是忽略preffix计算。我只解决了建议的一般问题……但这是非常低效的,因为它会经常重新计算深子树的和。@DanielWagner我认为你关于f
的假设是错误的,“问题是我无法访问节点的子节点,求和问题只是一个例子,而不是一般情况。您可以优化尾部计算(总和情况),但可以忽略预修复计算。我只解决了建议的一般问题。您可能会喜欢(使用幺半群作为向上移动的注释)。您可能会喜欢(使用幺半群作为向上移动的注释)。Traversable
绝对不够。我想在uniplate中有这样的东西,但我从来没有用过。你的方法看起来很明智。你介意写信给我吗libraries@haskell.org建议将其添加到数据树
?它明显地丢失了。@dfeur似乎容器
不是由库维护的;至少它没有被提及,而且Hackage上列出的维护人员在几个主要版本之前已经从库中更换了。也就是说,几周前,我与雷恩·罗马诺共同维护了容器。由于其在软件包层次结构中的特殊地位,以及大量的反向依赖关系,以前的软件包维护者已经建立了一种传统,即就API更改咨询库列表。一些特别适合API其余部分的添加可能会通过,但这是一个例外,而不是规则。Traversable
显然是不够的。我想在uniplate中有这样的东西,但我从来没有用过。你的方法看起来很明智。你介意写信给我吗libraries@haskell.org建议将其添加到数据树
?它明显地丢失了。@dfeur似乎容器
不是由库维护的;至少它没有被提及,而且Hackage上列出的维护人员在几个主要版本之前已经从库中更换了。也就是说,几周前,我与雷恩·罗马诺共同维护了容器。由于其在软件包层次结构中的特殊地位,以及大量的反向依赖关系,以前的软件包维护者已经建立了一种传统,即就API更改咨询库列表。一些特别适合API其余部分的添加可能会获得通过,但这是一个例外,而不是