Pattern Matching和Guards有什么区别?

我对Haskell和函数式编程都很新颖。 我的问题是非常基本的。 Pattern Matching和Guards有什么区别?

函数使用模式匹配

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

使用警卫的function

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

对我来说,模式匹配和守卫看起来基本相同。 两者都评估一个条件,如果为true,则会执行挂钩的expression式。 我的理解是否正确?

在这个例子中,我可以使用模式匹配或守卫来达到相同的结果。 但是有些事情告诉我,我错过了一些重要的东西。 我们可以总是replace一个吗?

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

事实上,他们从根本上是完全不同的! 至less在Haskell,无论如何。

卫兵既简单又灵活:它们本质上是一种特殊的语法,可以翻译成一系列if / thenexpression式。 你可以把任意的布尔expression式放在警卫中,但是他们不会做任何你不能做的事情。

模式匹配还有其他一些function:它们是解构数据唯一方法 ,它们在范围内绑定标识符 。 在同样的意义上,警卫等同于expression式,模式匹配等同于caseexpression式。 声明(无论是在顶层还是在letexpression式中)也是模式匹配的一种forms,“正常”定义与简单模式(单一标识符)相匹配。

模式匹配也往往是Haskell实际发生的主要方式 – 尝试解构模式中的数据是迫使评估的几件事之一。

顺便说一句,你可以在顶层声明中进行模式匹配:

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

这对于一组相关的定义偶尔是有用的。

GHC还提供了ViewPatterns扩展 ,这两种扩展结合在一起; 您可以在绑定上下文中使用任意函数,然后对结果进行模式匹配。 当然,这仍然是通常的语法糖。


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

  • 对于任何可以直接与一个或两个构造器直接匹配的东西,绝对使用模式匹配,在那里你并不真正关心复合数据,而是关心大部分结构。 @语法可以让你把整个结构绑定到一个variables上,同时对其进行模式匹配,但是在一个模式中做太多的操作会很快变得丑陋和不可读。

  • 当你需要根据一些与模式不完全对应的属性做出select时,绝对使用警卫,比如比较两个Int值来看哪个更大。

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

  • 如果你需要对不同模式表示的值做同样的事情,但是用一个方便的谓词对它们进行分类,使用带有警卫的单一通用模式通常更具可读性。 请注意,如果一组卫兵不是完全的,那么所有守卫都会失败的东西会下降到下一个模式(如果有的话)。 所以你可以把一个普通的模式和一些filter结合起来,以便抓住特殊情况,然后在其他方面进行模式匹配,以获得你所关心的细节。

  • 绝对不要使用卫兵来检查可以用模式检查的东西。 检查空列表是典型的例子,使用模式匹配。

  • 一般来说,如果有疑问,只要坚持模式匹配默认情况下,通常更好。 如果一个模式开始变得非常丑陋或者错综复杂,那就停下来考虑一下你可以怎么写。 除了使用警卫之外,其他选项还包括将子expression式作为单独的函数提取,或者在函数体中放入caseexpression式,以便将某些模式匹配向下推送到主要定义之外。

对我来说,模式匹配和守卫看起来基本相同。 两者都评估一个条件,如果为true,则会执行挂钩的expression式。 我的理解是否正确?

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

第二个模式匹配可以绑定variables。 所以虽然模式[]可能等同于guard null lst (不使用长度,因为它不等于 – 稍后会更多),模式x:xs当然不等同于guard not (null lst)因为这个模式绑定了variablesxxs ,而守卫则不xs

关于使用length :使用length来检查一个列表是否为空是非常不好的做法,因为要计算需要经过整个列表的长度,这将花费O(n)时间,而仅仅检查列表为空( O(1)时间为null或模式匹配)。 进一步使用`length'只是普通的不能在无限列表上工作。

首先,你可以把布尔expression式放在一个守卫中。

例如 :

就像列表parsing一样,布尔expression式可以在模式守卫中自由混合。 例如:

 fx | [y] <- x , y > 3 , Just z <- hy = ... 

更新

学习你一个Haskell有一个很好的引用关于差异:

模式是确保价值符合某种forms和解构它的一种方式,而守卫则是testing价值(或其中几个)的某些属性是真还是假的方法。 这听起来很像if语句,而且非常相似。 问题是,当你有几个条件时,守卫更可读,他们玩的模式非常好。

除了其他好的答案之外,我会尽量详细说明警卫:卫兵只是语法糖。 如果你仔细想想,你的程序中通常会有以下结构:

 fy = ... fx = if p(x) then A else B 

也就是说,如果一个模式匹配,紧随其后的是if-then-else歧视。 一名后卫直接将这种歧视折叠成模式匹配:

 fy = ... fx | p(x) = A | otherwise = B 

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

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

Interesting Posts