如何根据Haskell中的某个条件过滤树?

时间:2017-03-12 15:19:30

标签: haskell data-structures filter

我已经定义了一个树数据类型如下:

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

我希望根据特定条件过滤此树。我试着写的功能是:

filterT :: (a -> Bool) -> Tree a -> Tree a
filterT c (T(Leaf x,T(t1,t2))) = if c x 
                                then (T(Leaf x, T(filterT c t1, filterT c t2)))
                                else T(filterT c t1,filterT c t2)

此函数无法正常工作,因为在两个叶值(最后)都不满足条件的情况下,函数无法返回。如果你能提供帮助,我将不胜感激。

2 个答案:

答案 0 :(得分:3)

部分问题在于您根本无法表示空树。

-- Get rid of unnecessary tuple
data Tree a = Empty 
            | Leaf a 
            | T (Tree a) (Tree a) deriving (Show, Eq, Ord)

-- A filtered empty tree is still empty
filterT p Empty = Empty
-- A leaf either stays the same or becomes empty
filterT p (Leaf x) | p x = Leaf x
                   | otherwise = Empty
-- Any other tree is just the result of filtering each child
filterT p (T left right) = T (filterT p left) (filterT p right)

这不是一个很好的修复;它仍然为您提供了多种表示基本相同树的方法,因为EmptyT Empty Empty没有显着差异。您可以编写另一个函数来“修剪”这样的树。

prune :: Tree a -> Tree a
prune (T Empty Empty) = Empty
prune x = x

可以合并到filterT函数中,如下所示:

filterT _ Empty = Empty
filterT p (Leaf x) | p x = Leaf x
                   | otherwise = Empty
filterT p (T left right) = let prune (T Empty Empty) = Empty
                               prune x = x
                          in prune $ T (filterT p left) (filterT p right)

您也可以将prune扩展为将单叶树合约为一片叶子(Tree (Leaf 3) EmptyLeaf 3应该被视为同一棵树?),如

filterT' _ Empty = Empty
filterT' p (Leaf x) | p x = Leaf x
                    | otherwise = Empty
filterT' p (T left right) = let prune (T Empty Empty) = Empty
                                prune (T (Leaf x) Empty)) = Leaf x
                                prune (T Empty (Leaf x)) = Leaf x
                                prune x = x
                            in prune $ T (filterT p left) (filterT p right)

最后,您是否使用prune将取决于过滤后的树是否应该保留原始结构;也许你想区分叶子和曾经有两个孩子的节点,例如。

答案 1 :(得分:2)

整个树中的项目可能不满足条件。然后你将有一个树。因此,您必须将树的定义扩展为:

data Tree a = Empty | Leaf a | Node (Tree a) (Tree a)

即,包括Empty树的大小写或将filterT的签名更改为:

filterT :: (a -> Bool) -> Tree a -> Maybe (Tree a)

这样就可以返回Nothing

采用第二种方法(1),可能的方法是派生Foldable实例并根据右侧折叠定义过滤函数:

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

instance Foldable Tree where
  foldr f z (Leaf x)   = f x z
  foldr f z (Node l r) = foldr f (foldr f z r) l

filterT :: (a -> Bool) -> Tree a -> Maybe (Tree a)
filterT f = foldr go Nothing
  where
  go x z = if not (f x) then z else Just $ case z of
    Nothing -> Leaf x
    Just tr -> Node (Leaf x) tr

<子> 1.因为它使数据类型更简单,并避免冗余的Node Empty Empty值。