Haskell 与守卫的模式匹配

Haskell pattern matching with guards

假设我想在 Haskell 中使用

对树结构建模
data Tree = Null | Node Tree Integer Tree deriving Show

并且我想测试每个条目是否小于 10。我想我会使用模式匹配并编写

isSmall :: Tree -> Bool
isSmall _ 
  | Null = True
  | (Node a b c) = if b >= 10
                   then False
                   else isSmall a && isSmall c

但是它会给出有关 abc 超出范围的错误。我原以为将它们放在守卫中基本上会将它们放在范围内。这不是您应该如何在 Haskell 中进行模式匹配吗?我四处寻找可以指导我的示例,但我没有在使用由多个其他数据结构组成的数据结构的守卫中找到任何模式匹配示例。

错误:

test.hs:24:6: Not in scope: data constructor ‘Node’

test.hs:24:11: Not in scope: ‘a’

test.hs:24:13: Not in scope: ‘b’

test.hs:24:15: Not in scope: ‘c’

test.hs:24:27: Not in scope: ‘b’

test.hs:26:38: Not in scope: ‘a’

test.hs:26:57: Not in scope: ‘c’

如评论中所述,这是不正确的模式匹配。这是实现您似乎正在寻找的东西的一种方法:

isSmall :: Tree -> Bool
isSmall Null         = True
isSmall (Node a b c) = if b >= 10
                       then False
                       else isSmall a && isSmall c

按照您在问题中发布的方式进行操作,您还会遇到另一个错误:

* Couldn't match expected type `Bool' with actual type `Tree'
* In the expression: (Node a b c)
  In a stmt of a pattern guard for
                 an equation for `isSmall':
    (Node a b c)
  In an equation for `isSmall':
      isSmall _
        | Null = True
        | (Node a b c) = if b >= 10 then False else isSmall a && isSmall c

这表明 guard 语句中的表达式必须是 Bool 类型,但您提供的是 TreeNullNode)。

Is this not how you're supposed to do pattern matching in Haskell?

没有。守卫是布尔表达式,而不是模式。

你可以这样进行模式匹配:

isSmall :: Tree -> Bool
isSmall Null = True
isSmall (Node a b c) = b < 10 && isSmall a && isSmall c

...或者像这样:

isSmall :: Tree -> Bool
isSmall x = case x of
  Null -> True
  Node a b c -> b < 10 && isSmall a && isSmall c

...甚至像这样:

{-# LANGUAGE LambdaCase #-}

isSmall :: Tree -> Bool
isSmall = \case
  Null -> True
  Node a b c -> b < 10 && isSmall a && isSmall c

(使用 LambdaCase 语言扩展)。这可能最接近您最初的尝试。

也就是说,可以使用 <- 在守卫中嵌入模式。这被称为 "pattern guards":

isSmall :: Tree -> Bool
isSmall x 
  | Null <- x = True
  | Node a b c <- x = b < 10 && isSmall a && isSmall c

但是,这种语法在这里对您没有多大帮助。您仍然必须为参数命名(在本例中为 x),并且必须在任何地方明确地说出 <- x。直接使用模式匹配会更清晰(使用case或多重函数方程)