Haskell树上的褶皱变化

时间:2018-09-21 22:54:25

标签: haskell functional-programming binary-tree fold

给出一棵定义为的树:

data Tree a = Leaf | Node (Tree a) a (Tree a) deriving (Eq, Show)

我要使用该功能:

foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b
foldTree _ b Leaf         = b
foldTree f b (Node lt x rt) = f (foldTree f b lt) x (foldTree f b rt)

为了能够创建普通foldrfoldl的等效项,如下所示:

foldTreeR :: (a -> b -> b) -> b -> Tree a -> b
foldTreeL :: (b -> a -> b) -> b -> Tree a -> b

我认为这些定义非常简单,因为它们的定义几乎完全模仿了foldrfoldl的定义。我假设我将要做的就是以类似的方式类似地插入值,因此我将编写一个匿名函数,一个具有我的树的基本状态以及需要处理的树的累加器。 lambda函数必须根据完成的折叠类型而有所不同。

这是我想出的:

foldTreeR :: (a -> b -> b) -> b -> Tree a -> b
foldTreeR f acc t =  foldTree (\x acc -> f x acc) acc t

我得到了错误:

Couldn't match type ‘a’ with ‘a -> b’ 
      ‘a’ is a rigid type variable bound by
        the type signature for:
          foldTreeR :: forall a b. (a -> b -> b) -> b -> Tree a -> b
        at Folds.hs:294:14
      Expected type: Tree (a -> b)
        Actual type: Tree a

在这种情况下,我不确定如何传递原始树。

似乎左折叠只是对lambda函数中的值进行了重新排序以及对其进行了不同评估而产生的变化。

有人可以帮助我了解如何在此处找到解决方案吗?

2 个答案:

答案 0 :(得分:3)

我们可以通过折叠成 endofunctions 来从数据类型跟随的树形折叠中恢复线性的,累加器传递的折叠,如下所示:

data Tree a = Leaf | Node (Tree a) a (Tree a) deriving (Eq, Show)

-- foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b

foldTreeR :: (a -> r -> r) -> r -> Tree a -> r
foldTreeR cons z t = foldTree g id t z           -- b ~ r -> r
  where
  g lt a rt = lt . cons a . rt 

左折:

foldTreeL :: (acc -> a -> acc) -> acc -> Tree a -> acc
foldTreeL conj z t = foldTree g id t z           -- b ~ acc -> acc
  where
  g lt a rt = rt . flip conj a . lt

更详细的解释:

cons aflip conj a的类型均为r -> r(或acc -> acc,类型相同)。这是函数的类型,参数类型和结果类型相同。

这类功能称为内在功能, endo 表示其域和共域(箭头右侧和左侧的类型)的相同性。这样,他们可以轻松地撰写:可以参与(.)操作,即函数组合,组合的结果与操作数的类型相同:

(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)

-- and for endofunctions,
-- ((f :: r -> r) . (g :: r -> r)) :: r -> r

对于顺序遍历[a,b,c,...,n]的树,右折将其变成合成

(cons a . cons b . cons c . ..... . cons n) z

-- which is the same as
-- cons a (cons b (cons c ... (cons n z) ... ))

左折将其变成

(conj' n . ..... . conj' c . conj' b . conj' a) z

其中

conj' a acc = flip conj a acc = conj acc a     -- by definition of `flip`

-- so the above composition chain is the same as
-- conj (... (conj (conj (conj z a) b) c) ...) n

在该链上散布着一些id,每个Leaf变成了id,对整个链没有影响,因为

(id . f) x = id (f x) = f x = f (id x) = (f . id) x

如此

id . f = f = f . id

id用作w的“零”元素。就像0+操作一样(顺便说一下,这是由{{1}组成的'monoid' }和.,或id0)。


这是我们如何为树创建该顺序遍历列表的方法:

+

因此列表inorder :: Tree a -> [a] inorder t = foldTree g [] t where g lt a rt = lt ++ [a] ++ rt 实际上是创建为

[a,b,...,n]

,该树的右折将被创建为

[] ++ [a] ++ [] ++ [b] ++ [] ++ ... ++ [n] ++ []

答案 1 :(得分:2)

以下是您自己提出解决方案的方法。

我们有

data Tree a =                               Leaf 
            | Node (Tree a) a (Tree a)                   deriving (Eq, Show)

foldTree :: (        b ->   a ->   b -> b) -> b -> Tree a -> b

foldTree    (g :: b ->   a ->   b -> b) (z :: b) :: Tree a -> b

因此,给定gz时,此函数通过将Tree a的子树转换为{{1来将b的值转换为t的值}},并通过b将它们与树的a组合在一起。

我们可以使用g在这些树上 map 吗?是的:

foldTree

现在我们有了mapTree :: (a -> c) -> Tree a -> Tree c mapTree f t = foldTree g z t where -- we need to create a Node with the mapped element inside it -- already having the transformed sub-trees. Well, -- creating Tree values is the job of that type's data constructors: g lt a rt = Node lt (f a) rt -- f is applied to the element `a` -- and all leaves are transformed into the same value, which is: z = Leaf 。这对我们有什么帮助?

我们想要什么?我们想要

mapTree (f :: a -> c) :: Tree a -> Tree c

因此我们有foldTreeR :: (a -> b -> b) -> b -> Tree a -> b -- i.e. foldTreeR (cons :: a -> r -> r) (nil :: r) :: Tree a -> r ,如cons

如果我们将cons (x :: a) :: r -> r 映射到树上怎么办?类型cons实际上是a -> r -> r,实际上我们刚刚看到a -> (r -> r)cons变成(x :: a)读取它:将值{ {1}}或将r -> r输入类型x 的值):

a

我们为什么要那个?我们现在在树的节点中拥有的所有r -> r函数如何处理?好吧,我们可以通过有序遍历将一棵树变成其节点中的值的列表:

mapTree (cons :: a -> r -> r) :: Tree a -> Tree (r -> r)

所以我们可以拥有

r -> r

,我们可以线性组成列表中的所有这些函数,以的顺序进行从右到左的结果传递操作 ... 右折!

inorder :: Tree d -> [d]
inorder t = foldTree (\l a r -> l ++ a : r) [] t

就是这样。

如果我们内联所有内容并简化结果定义,我们将在另一个答案中得到答案。

与左折相同。试试吧。