现在我有树数据类型:
data TernaryTree a = EmptyTree
| Node a (TernaryTree a) (TernaryTree a) (TernaryTree a)
deriving (Show)
我正在尝试创建一个可以在三元树中循环一个值的函数。 树没有排序。
treeLook :: (Ord a)=> a -> TernaryTree a -> Bool
treeLook x EmptyTree = False
treeLook x (Node a left mid right) =
if x /= a
then do
treeLook x left
treeLook x mid
treeLook x right
else
True
我现在有这个,但我无法编译它。它说:
Couldn't match expected type "m0 b0" with actual type "bool" on the line:
treeLook x right
答案 0 :(得分:5)
do
是一个关键字,用于为 monads 启用一些语法糖。在这个引用中,不需要monad。
此处有两种情况:EmptyTree
表示我们找不到值,因此我们返回False
。
对于Node
,我们最多可以检查四个条件:是我们查找的值的值,是第一个子树中的值,是第二个子树中的值,是第三个子树中的值子树。从其中一项检查True
开始,我们可以返回True
。如果所有检查都失败,我们返回False
,这是逻辑或(||
)的行为。所以我们可以这样写:
treeLook :: Eq a => a -> TernaryTree a -> Bool
treeLook _ EmptyTree = False
treeLook x (Node a l m r) = x == a || treeLook x l || treeLook x m || treeLook x r
或者我们可以定义一个本地范围的函数,阻止我们递归传递我们正在寻找的值:
treeLook :: Eq a => a -> TernaryTree a -> Bool
treeLook x = go
where go EmptyTree = False
go (Node a l m r) = x == a || go l || go m || go r
请注意,由于树没有排序,我们不需要Ord a
类型约束,我们只需要检查相等性(这里是x == a
),所以Eq a
类型约束已经足够了。
答案 1 :(得分:4)
做的是monads。
而是使用any。
treeLook _ EmptyTree = False
treeLook x (Node y l m r) = any id [x == y, look l, look m, look r]
where look = treeLook x
正如所指出的,或者更好用。
treeLook x (Node y l m r) = or [x == y, look l, look m, look r]
where look = treeLook x
我最喜欢的是:
treeLook _ EmptyTree = False
treeLook x (Node y l m r) = x == y || any (treeLook x) [l, m, r]
答案 2 :(得分:3)
一个选项是使用elem
,它会检查值是否是Foldable
容器的元素。
treeLook :: Eq a => a -> TernaryTree a -> Bool
treeLook = elem
现在你只需要写一个Foldable
的实例。一种选择是启用DeriveFoldable
扩展名,只需使用deriving (Show, Foldable)
让GHC为您编写实例。但是你不会那么学习。因此,让我们探索一些方法来实现它。
-- This import is needed for non-bleeding-edge GHC versions.
import Data.Monoid ((<>))
instance Foldable TernaryTree where
-- Fold over the tree in pre-order
foldMap _ EmptyTree = mempty
foldMap f (Node x ls cs rs) =
f x <> foldMap f ls <> foldMap f cs <> foldMap f rs
但重复的foldMap
本身就是一种&#34;模式&#34;你可以写出&#34; catamorphism&#34;树木:
cataTree :: r -> (a -> r -> r -> r -> r) -> TernaryTree a -> r
cataTree e _ EmptyTree = e
cataTree e f (Node x ls cs rs) =
f x (cataTree e f ls) (cataTree e f cs) (cataTree e f rs)
现在您可以定义foldMap
:
foldMap f = cataTree mempty (\a lr cr rr -> f a <> lr <> cr <> rr)
现在foldMap
本身有一个更强大的堂兄traverse
。所以另一种选择是从那里开始:
import Data.Traversable (fmapDefault, foldMapDefault)
instance Traversable TernaryTree where
-- Traverse the tree in pre-order
traverse f =
cataTree (pure EmptyTree)
(\a ls cs rs -> Node <$> f a <*> ls <*> cs <*> rs)
instance Functor TernaryTree where
fmap = fmapDefault
instance Foldable TernaryTree where
foldMap = foldMapDefault
显然,你还没有想要做任何如此花哨的事情,但是一旦你想要产生一堆这样的技术,这种技术实际上是有用的功能很快。