Haskell:在三元树中查找值,树未排序

时间:2018-03-10 21:05:46

标签: haskell tree ternary-tree

现在我有树数据类型:

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

3 个答案:

答案 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

显然,你还没有想要做任何如此花哨的事情,但是一旦你想要产生一堆这样的技术,这种技术实际上是有用的功能很快。