Pattern Matching和Guards有什么区别?

时间:2010-11-11 16:33:35

标签: haskell syntax guard

我对Haskell和一般的函数式编程都很陌生。我的问题非常基本。 Pattern Matching和Guards之间有什么区别?

使用模式匹配的功能

check :: [a] -> String
check [] = "Empty"
check (x:xs) = "Contains Elements"

使用警卫的功能

check_ :: [a] -> String
check_ lst
    | length lst < 1 = "Empty"
    | otherwise = "Contains elements"

对我而言,模式匹配和防护看起来基本相同。两者都评估条件,如果为true则将执行挂钩的表达式。我的理解是否正确?

在这个例子中,我可以使用模式匹配或防护来获得相同的结果。但是有些东西告诉我,我错过了一些重要的事情。我们可以一直替换另一个吗?

有人可举例说明模式匹配优于警卫,反之亦然吗?

4 个答案:

答案 0 :(得分:57)

实际上,它们基本上是完全不同的!至少在Haskell中,无论如何。

Guards既简单又灵活:它们本质上只是特殊语法,可转换为一系列if / then表达式。你可以在守卫中放置任意布尔表达式,但是他们不能做任何你不能用常规if做的事情。

模式匹配会做一些额外的事情:它们是解构数据的唯一方法,并且绑定其范围内的标识符。与保护等同于if表达式的意义相同,模式匹配等同于case表达式。声明(在顶层或类似let表达式)也是一种模式匹配形式,“普通”定义与普通模式匹配,即单个标识符。

模式匹配也往往是Haskell中实际发生的主要方式 - 试图解析模式中的数据是迫使评估的少数事情之一。

顺便说一句,您实际上可以在顶级声明中进行模式匹配:

square = (^2)

(one:four:nine:_) = map square [1..]

这有时对一组相关定义很有用。

GHC也provides the ViewPatterns extension这两种结合起来;您可以在绑定上下文中使用任意函数,然后在结果上进行模式匹配。当然,这仍然只是通常情况下的语法糖。


至于在哪里使用的日常问题,这里有一些粗略的指南:

  • 对于任何可以直接匹配一个或两个构造函数的东西,绝对使用模式匹配,在那里你并不真正关心整个复合数据,而是关心大部分结构。 @语法允许您将整体结构绑定到变量,同时还对其进行模式匹配,但在一种模式中执行太多操作可能会很快变得难看且无法读取。

  • 当您需要根据某些与图案不完整对应的属性做出选择时,绝对使用防护,例如:比较两个Int值,看哪哪个更大。

  • 如果您只需要来自大型结构内部的几个数据,特别是如果您还需要将结构作为一个整体使用,那么守卫和访问器函数通常比一些充满{的怪异模式更具可读性。 {1}}和@

  • 如果您需要对由不同模式表示的值执行相同的操作,但使用方便的谓词对它们进行分类,则使用带保护的单个通用模式通常更具可读性。请注意,如果一组防护措施不是详尽无遗的,那么所有防护措施失败的东西都将下拉到下一个模式(如果有的话)。因此,您可以将常规模式与某些过滤器结合使用以捕获异常情况,然后对其他所有内容进行模式匹配以获取您关注的详细信息。

  • 绝对不要使用防护措施来处理那些可以用模式轻易检查的东西。检查空列表是典型的例子,使用模式匹配。

  • 一般来说,如果有疑问,默认情况下坚持使用模式匹配,通常会更好。如果一个模式开始变得非常丑陋或令人费解,那么请停下来考虑如何编写它。除了使用警卫之外,其他选项包括将子表达式作为单独的函数提取或将_表达式放在函数体内,以便将一些模式匹配向下推到它们之外并超出主要定义。

答案 1 :(得分:10)

  

对我而言,模式匹配和防护看起来基本相同。两者都评估条件,如果为true则将执行挂钩的表达式。我的理解是否正确?

不完全。第一种模式匹配不能评估任意条件。它只能检查是否使用给定的构造函数创建了值。

第二个模式匹配可以绑定变量。因此虽然模式[]可能等同于保护null lst(不使用长度,因为它不等同 - 稍后会更多),模式x:xs肯定不等同保护not (null lst),因为该模式绑定了变量xxs,而后卫则没有。

关于使用length的说明:使用length来检查列表是否为空是非常糟糕的做法,因为要计算完整列表所需的长度,这将需要{ {1}}时间,只需检查列表是否为空,O(n)时间O(1)或模式匹配。进一步使用`length'只是简单无法在无限列表上工作。

答案 2 :(得分:9)

首先,你可以将布尔表达式放在一个守卫中。

For example

  

与列表推导一样,布尔表达式可以在模式保护中自由混合。例如:

f x | [y] <- x
    , y > 3
    , Just z <- h y
    = ...

<小时/> 更新

Learn You a Haskell有一个关于差异的引用:

  

虽然模式是确保某个值符合某种形式并对其进行解构的一种方式,但是保护是一种测试某个值(或其中几个)的属性是真还是假的方法。这听起来很像if语句,而且非常相似。事情是,当你有几个条件并且它们与模式非常匹配时,守卫会更具可读性。

答案 3 :(得分:5)

除了其他好的答案之外,我将尝试具体讲述警卫:警卫只是语法糖。如果您考虑一下,您的程序通常会有以下结构:

f y = ...
f x =
  if p(x) then A else B

也就是说,如果模式匹配,则紧跟在if-then-else之后。一名警卫直接将这种歧视折叠成模式匹配:

f y = ...
f x | p(x) = A
    | otherwise = B

otherwise在标准库中定义为True。它比if-then-else链更方便,有时它也使代码变得更简单,因此它比if-then-else结构更容易编写。

换句话说,它是另一种结构上的糖,在很多情况下可以大大简化你的代码。你会发现它消除了许多if-then-else链并使你的代码更具可读性。