我需要在二叉树中找到所有可能的子树:
allSubtrees :: BinaryT a -> [BinaryT a]
allSubtrees = undefined
树是:
data BinaryT a =
Empty
| Node (BinaryT a) a (BinaryT a)
deriving (Eq, Show)
我是Haskell的新手,我知道Haskell中没有while
/ for
循环。 Haskell就是递归。我的问题是,如何在没有无限递归的情况下获得树的所有可能子树?
答案 0 :(得分:6)
bheklilr给出了对你的问题的一种解释的答案,但是我会告诉你作为一个初学者,你将从自己解决问题中受益:
首先确保您已明确定义了您希望功能执行的操作。我假设您希望它像tails
一样工作。
然后认为以声明方式,其中=
- 符号表示“是”,并写两个语句。第一个应该是“allSubtrees
树的Empty
是......”(这是你的基本情况):
allSubtrees Empty = ...
然后你的递归案例,阅读allSubtrees
的{{1}}是......“:
Node
如果你无法解决这个问题,请尝试编写一个适用于allSubtrees (Node l a r) = ...something combining the subTrees of l and the subtrees of r
的递归函数,然后对其进行概括。
答案 1 :(得分:3)
Uniplate是你的朋友,在这里:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Generics.Uniplate.Data (universe)
import Data.Data (Data)
import Data.Typeable (Typeable)
data BinaryT a =
Empty
| Node (BinaryT a) a (BinaryT a)
deriving (Eq, Show, Typeable, Data)
allSubtrees :: (Data a, Typeable a) => BinaryT a -> [BinaryT a]
allSubtrees = universe
答案 2 :(得分:3)
由于已经存在Uniplate演示,因此为了完整起见,使用recursion-schemes
库是一个实现:
{-# LANGUAGE DeriveFunctor, TypeFamilies #-}
import Data.Functor.Foldable
data BinaryT a
= Empty
| Node (BinaryT a) a (BinaryT a)
deriving (Eq, Show)
data BinaryTBase a b
= BaseEmpty
| BaseNode b a b
deriving (Functor)
type instance Base (BinaryT a) = BinaryTBase a
instance Foldable (BinaryT b) where
project Empty = BaseEmpty
project (Node a b c) = BaseNode a b c
instance Unfoldable (BinaryT b) where
embed BaseEmpty = Empty
embed (BaseNode a b c) = Node a b c
allSubtrees :: BinaryT a -> [BinaryT a]
allSubtrees = para phi where
phi BaseEmpty = []
phi (BaseNode (l, ll) v (r, rr)) = ll ++ rr ++ [Node r v l]
基础仿函数样板很大,但相对不足为奇,并且可以节省您的长期工作量,因为它是每种类型的一次。
这是使用geniplate
库的另一个实现:
{-# LANGUAGE TemplateHaskell #-}
import Data.Generics.Geniplate
data BinaryT a =
Empty
| Node (BinaryT a) a (BinaryT a)
deriving (Eq, Show)
allSubTrees :: BinaryT a -> [BinaryT a]
allSubTrees = $(genUniverseBi 'allSubTrees)
这是@bheklilr显式递归方法的缩短版本,人们可能期望从一个新手(我使用(++)
进行对称):
allSubTrees3 :: BinaryT a -> [BinaryT a]
allSubTrees3 Empty = []
allSubTrees3 this @ (Node left _ right) = [this] ++ leftSubs ++ rightSubs where
leftSubs = allSubTrees3 left
rightSubs = allSubTrees3 right
请注意,它列出了根,但未列出空子树,但它很容易更改。
我想知道不同方法的优点和缺点是什么。 uniplate
在某种程度上或多或少比其他方法安全吗?
请注意,recursion-schemes
方法既简洁(如果您需要对一种类型进行多次不同的遍历)又灵活(您可以完全控制遍历顺序,是否包含空子树等)。一个缺点是para
和其他方案的类型太笼统,不允许类型推断,因此通常需要类型签名来消除歧义。
geniplate
似乎比uniplate
更少侵扰,因为没有必要放deriving
条款。
答案 3 :(得分:2)
您可以非常轻松地使用递归来解决此问题。可能比使用循环容易。
allSubTrees :: BinaryT a -> [BinaryT a]
allSubTrees Empty = []
allSubTrees (Node Empty n Empty) = []
allSubTrees (Node Empty n right) = right : allSubTrees right
allSubTrees (Node left n Empty) = left : allSubTrees left
allSubTrees (Node left n right) = left : right : leftSubs ++ rightSubs
where
leftSubs = allSubTrees left
rightSubs = allSubTrees right
答案 4 :(得分:1)
除了nponeccop的解决方案之外,这里是树的广度优先行走(不可能与paramorphism;真正需要共同递归):
{-# LANGUAGE DeriveFunctor, TypeFamilies #-}
import Data.Functor.Foldable
data BinaryT a
= Empty
| Node (BinaryT a) a (BinaryT a)
deriving (Eq, Show)
allSubtrees :: BinaryT a -> [BinaryT a]
allSubtrees t = ana phi [t] where
phi [] = Nil
phi (Empty:t) = Cons Empty t
phi (n@(Node l v r):t) = Cons n (t++(l:[r]))
main = print $ allSubtrees $
Node (Node Empty "a" Empty) "b" (Node (Node Empty "c" Empty) "d" Empty)