【问题标题】:Mapping over a Tree映射一棵树
【发布时间】:2015-10-01 23:03:52
【问题描述】:

我是 Haskell 的新手,为了练习,我一直在使用 foldrfoldl 实现一堆函数(maplength 等)。现在我想移动到树上!

我的树数据结构:

data Tree a = Node a [Tree a] deriving (Show) 

我写了一个 Haskell 函数来折叠树:

treeFold :: (b->a->b) -> b -> Tree a -> b
treeFold f s (Node a []) = f s a
treeFold f s (Node a xs) = foldl f (f s a) (dFSList xs)

其中dFSList 是树中所有节点的列表。所以做类似的事情:

treeFold (+) 0 (Node 1 [Node 2 [], Node 3 []])

返回 6. 酷。

现在我想编写一个 Haskell 函数来映射树,但我想使用我的 treeFold 函数来完成它。这是我目前所拥有的:

treeMap f (Node a []) = (Node (f a) [])
treeMap f (Node a (x:xs)) = (Node (f a) (a list involving foldTree somehow??))

如何完成我的treeMap 函数?我希望能够做到

treeMap (+1) (Node 1 [Node 2 [], Node 3 []])

它应该返回

(Node 2 [Node 3 [], Node 4 []])

【问题讨论】:

  • 您将树转换为平面列表的方式(可能具有任意顺序 w.r.t 树的形状)然后对其进行常规列表折叠意味着折叠不是特定于树的,即您的折叠功能不能考虑树的形状,只能以任意顺序查看内容。

标签: haskell dictionary tree fold


【解决方案1】:

拆开树木的方法通常是使用不同种类的折叠(由于我不知道的某些类别理论原因,通常称为变质)。你有data Tree a = Node a [Tree a],所以要把它拆开,你做

cat :: (a -> [b] -> b) -> Tree a -> b
cat f (Node root children) = f root (map (cat f) children)

好吗?所以现在(使用标准的Functor 类而不是专门的treeMap),

instance Functor Tree where
  fmap f = cat (Node . f)

你也可以像这样写线性折叠。你的treeFold 让我很困惑,但你可以写,例如,

instance Foldable Tree where
  foldMap f = cat go where
    go root children = f root `mappend` fold children

更强大,

instance Traversable Tree where
  traverse f = cat go where
    go root children = Node <$> f root <*> sequenceA children

【讨论】:

  • 有趣。最初,我写了类似你的 cat 函数的东西,但我被它打扰了,因为我认为它不是很像折叠。很高兴知道这也许是解决问题的唯一方法
  • 我相信catamorphism这个词来自与catastrophe相同的(希腊语,我猜)前缀cata-,意思是“破坏,分解”,正如你在上面写的。 Morphism 表示变形、变形。所以catamorphism 只是一个“分解的转换”。
  • @ilikecats 将数据结构表示为函数的“Church 编码”如下所示:(1)对于数据结构中的每个选项(由| 分隔),从这些选项中创建一个函数一些未知的论点b; (2) 当你看到数据结构的递归时,接受b 作为参数; (3) 从这些参数函数中创建一个函数到所说的b。所以Either x y 变成了折叠(x -&gt; b) -&gt; (y -&gt; b) -&gt; bdata BinLeaf x y = Bin x (Tree x y) (Tree x y) | Leaf y 成为折叠 (x -&gt; b -&gt; b -&gt; b) -&gt; (y -&gt; b) -&gt; b 等。
  • @ChrisDrost 我不会撒谎的,克里斯。我在这里有点困惑。查看 1,我发现我想为每个选项创建一个函数,由 | 分隔。 .但是我的数据结构并没有以这种方式分离。你能详细说明你的意思吗?
  • 如果不清楚,我试图通过解释 flip cat tree 是一个函数 (x -&gt; [b] -&gt; b) -&gt; b 来解释为什么 cat :: (x -&gt; [b] -&gt; b) -&gt; Tree x -&gt; b 在某种意义上是“正确的折叠函数”致tree:一个人能做的事,另一个人能做的事。你的treeFold 是有问题的,因为它试图将其建模为b -&gt; (b -&gt; a -&gt; b) -&gt; b,我们可以将其识别为[a] 类型,换句话说,你的treeFold 是函数toList = foldMap (:[]) :: (Foldable t) =&gt; t x -&gt; [x],它通过核对树的结构“压平”它。
【解决方案2】:

你不需要treeFold 来写像treeMap 这样的东西。

treeMap f (Node a xs) = Node (f a) (map (treeMap f) xs)

对应相同思想的基类是FoldableFunctor。基础中的所有Foldable 已经是Functor;能够独立于treeFold 定义treeMap 是很自然的。

【讨论】:

  • 如果我想怎么办?有办法吗?
  • 我不认为你可以用你对treeFold的定义。当您在树枝上调用 dFSList 时,它会丢失所有的树结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-04
  • 1970-01-01
  • 1970-01-01
  • 2021-01-09
  • 2015-12-20
  • 1970-01-01
  • 2010-10-21
相关资源
最近更新 更多