Riccardo は正しいです。GHC は、ガードがすべて false である可能性はないと推測していません。だから彼の答えを受け入れてください。
脱線して、コーディング スタイルについて話します。
使用しない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
の 3 つの値しかないため、GHC はすべての可能性をカバーしたことを確認でき、人間の読者はそれらを正しくカバーしたことを簡単に確認できます。EQ
LT
GT
これは、より効率的なコードでもあります。呼び出してcompare
から呼び出すのではなく、1 回==
呼び出し<
ます。
ここで、もう少し脱線して、怠惰について話します。
おそらく、次のような関数も書いたことがあるでしょう:
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
これで、ビットをできるだけ早く返します --- たとえ比較でエラーがスローされたとしても! --- それでも、比較は最大で 1 回実行します。