使用二叉树

时间:2017-05-15 13:35:45

标签: haskell pattern-matching

我是Haskell的初学者,在理解我得到的警告方面遇到了一些麻烦。我已经实现了二叉树,

data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Eq, Show, 
Read)

它工作正常,但我在此代码上发出不完整的模式警告

get :: Ord a => a -> Tree a -> Maybe a
get _ Nil = Nothing
get x (Node v lt rt)
    | x == v = Just x
    | x < v = get x lt
    | x > v = get x rt

它希望我匹配的模式是_ (Node _ _ _ )。我不确定这种模式是什么意思?

2 个答案:

答案 0 :(得分:3)

这里有两个问题。首先,数据类型:

data Tree a = Nil | Node a (Tree left) (Tree right) deriving (Eq, Show, Read)
--                               ^ left?     ^ right?

在您的数据定义中,您使用 leftright ,但这些不是在数据定义的头部定义因此,这些不是类型参数。你可能想说:

data Tree a =   Nil
              | Node { value :: a, left :: Tree a, right :: Tree a}
              deriving (Eq, Show, Read)

但现在我们仍然收到错误:

hs.hs:5:1: Warning:
    Pattern match(es) are non-exhaustive
    In an equation for ‘get’: Patterns not matched: _ (Node _ _ _)
Ok, modules loaded: Main.

这里的问题是 Haskell不知道两个值只能是<==>

如果你写instance Ord,那么你有一个“联系人”,你将定义总排序。换句话说,对于任何两个值xy,它都包含x < yx > yx == y。然而问题是Haskell 知道这一点。对于Haskell,任何函数(<)(==)(>)都可以生成TrueFalse。因此 - 由于编译器始终保守 - 它会考虑有两个值的情况,即所有x < yx == yx > y都会失败(比如你假设会写foo x ybar x yqux x y然后这肯定会发生,因为它们是三个 blackbox 函数。您可以通过在最后一种情况下撰写otherwise 来解决此问题:

get :: Ord a => a -> Tree a -> Maybe a
get _ Nil = Nothing
get x (Node v lt rt)
    | x == v = Just x
    | x < v = get x lt
    | otherwise = get x rt

otherwiseTrue别名,因此不可能不接受该分支。所以现在保守编译器理解,无论xy的值是什么,它总是需要一些分支,因为如果它没有采取前两个,它将当然采取最后一个。

您可能认为这很奇怪,但由于合同通常没有以正式语言指定(仅在文档中,因此是自然语言),编译器 no < / em>意味着要知道:作为程序员,您可以决定不尊重合同(但请注意,这是非常坏主意)。即使你通常作为程序员编写正式合同,你仍然可以决定不尊重它,而且编译器也不能总是对正式合同做必要的逻辑推理。

答案 1 :(得分:2)

Willem Van Onsem已经很好地解释了这个问题。我只想补充一点, 可以在xv之间进行比较,其方式与发布的代码非常相似,但其分支却被详尽无遗的编译器。

而不是

get :: Ord a => a -> Tree a -> Maybe a
get _ Nil = Nothing
get x (Node v lt rt)
    | x == v = Just x
    | x < v = get x lt
    | x > v = get x rt

只需使用

get :: Ord a => a -> Tree a -> Maybe a
get _ Nil = Nothing
get x (Node v lt rt) = case compare x v of
    EQ -> Just x
    LT -> get x lt
    GT -> get x rt

实际上,compare是一个带有两个参数的函数,并在枚举类型Ordering中返回一个值,该值只能是EQ(相等),LT(更少)比)和GT(大于)。由于这是一个代数类型,GHC可以看到它的所有构造函数都由case处理。

此外,根据实际类型a,使用compare可能会更有效率。例如,当比较两个可能很长的字符串时,它是次优的,以便遍历它们两次(如果不是原始代码中的三次):compare只对一个字符串进行一次传递并确定哪个顺序关系成立