当我使用GHC编译以下代码时(使用-Wall
标志):
module Main where
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
insert :: (Ord a) => a -> Tree a -> Tree a
insert x EmptyTree = Node x EmptyTree EmptyTree
insert x (Node a left right)
| x == a = Node a left right
| x < a = Node a (insert x left) right
| x > a = Node a left (insert x right)
main :: IO()
main = do
let nums = [1..10]::[Int]
print . foldr insert EmptyTree $ nums
GHC抱怨insert
中的模式匹配并非详尽无遗:
test.hs|6| 1:
|| Warning: Pattern match(es) are non-exhaustive
|| In an equation for `insert': Patterns not matched: _ (Node _ _ _)
为什么GHC会发出此警告?很明显,GHC抱怨的模式是在insert x (Node a left right)
处理的。
答案 0 :(得分:49)
这是因为模式匹配不完整。我无法保证x==a
,x<a
或x>a
中的一个成立。例如,如果类型为Double
且x
为NaN,那么它们都不是True
。
答案 1 :(得分:39)
Riccardo是正确的,GHC并不认为你的警卫不可能都是假的。请接受他的回答。
我要离题并谈论编码风格。
您不使用otherwise
的动机可能是看起来不雅观:
insert :: (Ord a) => a -> Tree a -> Tree a
insert x EmptyTree = Node x EmptyTree EmptyTree
insert x (Node a left right)
| x == a = Node a left right
| x < a = Node a (insert x left) right
| otherwise = Node a left (insert x right)
看一下这段代码,一个人类读者必须向自己确认最后的守卫恰好接受x > a
的情况。
我们可以这样写:
insert :: (Ord a) => a -> Tree a -> Tree a
insert x EmptyTree = Node x EmptyTree EmptyTree
insert x (Node a left right) = case x `compare` a of
EQ -> Node a left right
LT -> Node a (insert x left) right
GT -> Node a left (insert x right)
Ordering
返回的compare
类型只有三个值EQ
,LT
和GT
,因此GHC可以确认您已覆盖一切可能性,人类读者可以很容易地看到你已经正确地覆盖了它们。
这也是效率更高的代码:我们只调用compare
一次,而不是调用==
,然后也可能调用<
。
现在我要离题了一些,谈论懒惰。
您可能还编写了类似于此的函数:
contains :: (Ord a) => a -> Tree a -> Bool
contains _ EmptyTree = False
contains x (Node a left right) = case x `compare` a of
EQ -> True
...
当x == a
时,您需要知道树使用Node
构造函数,并且其第一个参数等于x
。您不需要知道任何一个子树是什么。
但现在回过头来看我对insert
的定义。当给出的树是Node
时,它总是返回Node
,其第一个参数始终为a
。但它没有预先说明:而是评估x `compare` a
。
我们可以重写insert
以尽可能晚地执行比较:
insert :: (Ord a) => a -> Tree a -> Tree a
insert x EmptyTree = Node x EmptyTree EmptyTree
insert x (Node a left right) = Node a newLeft newRight
where comparison = x `compare` a
newLeft = if comparison == LT then insert x left else left
newRight = if comparison == GT then insert x right else right
现在我们尽快返回Node a
位 - 即使比较会引发错误! ---我们最多只进行一次比较。
答案 2 :(得分:26)
GHC无法推断insert x (Node a left right)
中的三名警卫是否涵盖了所有可能的案件,因此没有任何机构与insert x (Node a left right)
相关联。尝试使用x > a
(otherwise
的synonim)替换最后一个条件True
。
然而,在这种特殊情况下,守卫并没有覆盖所有情况,请参阅augustss关于双数的例子。