给出一棵定义为的树:
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)
为了能够创建普通foldr
和foldl
的等效项,如下所示:
foldTreeR :: (a -> b -> b) -> b -> Tree a -> b
foldTreeL :: (b -> a -> b) -> b -> Tree a -> b
我认为这些定义非常简单,因为它们的定义几乎完全模仿了foldr
和foldl
的定义。我假设我将要做的就是以类似的方式类似地插入值,因此我将编写一个匿名函数,一个具有我的树的基本状态以及需要处理的树的累加器。 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函数中的值进行了重新排序以及对其进行了不同评估而产生的变化。
有人可以帮助我了解如何在此处找到解决方案吗?
答案 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 a
和flip 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' }和.
,或id
和0
)。
这是我们如何为树创建该顺序遍历列表的方法:
+
因此列表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
因此,给定g
和z
时,此函数通过将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
就是这样。
如果我们内联所有内容并简化结果定义,我们将在另一个答案中得到答案。
与左折相同。试试吧。