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式,模式匹配等同于case
expression式。 声明(无论是在顶层还是在let
expression式中)也是模式匹配的一种forms,“正常”定义与简单模式(单一标识符)相匹配。
模式匹配也往往是Haskell实际发生的主要方式 – 尝试解构模式中的数据是迫使评估的几件事之一。
顺便说一句,你可以在顶层声明中进行模式匹配:
square = (^2) (one:four:nine:_) = map square [1..]
这对于一组相关的定义偶尔是有用的。
GHC还提供了ViewPatterns扩展 ,这两种扩展结合在一起; 您可以在绑定上下文中使用任意函数,然后对结果进行模式匹配。 当然,这仍然是通常的语法糖。
至于在哪里使用的日常问题,这里有一些粗略的指南:
-
对于任何可以直接与一个或两个构造器直接匹配的东西,绝对使用模式匹配,在那里你并不真正关心复合数据,而是关心大部分结构。
@
语法可以让你把整个结构绑定到一个variables上,同时对其进行模式匹配,但是在一个模式中做太多的操作会很快变得丑陋和不可读。 -
当你需要根据一些与模式不完全对应的属性做出select时,绝对使用警卫,比如比较两个
Int
值来看哪个更大。 -
如果你只需要一个大型结构内部的一些数据,特别是如果你还需要使用这个结构作为一个整体,那么守卫和访问函数通常比一些充满
@
和_
怪异模式更具可读性。 -
如果你需要对不同模式表示的值做同样的事情,但是用一个方便的谓词对它们进行分类,使用带有警卫的单一通用模式通常更具可读性。 请注意,如果一组卫兵不是完全的,那么所有守卫都会失败的东西会下降到下一个模式(如果有的话)。 所以你可以把一个普通的模式和一些filter结合起来,以便抓住特殊情况,然后在其他方面进行模式匹配,以获得你所关心的细节。
-
绝对不要使用卫兵来检查可以用模式检查的东西。 检查空列表是典型的例子,使用模式匹配。
-
一般来说,如果有疑问,只要坚持模式匹配默认情况下,通常更好。 如果一个模式开始变得非常丑陋或者错综复杂,那就停下来考虑一下你可以怎么写。 除了使用警卫之外,其他选项还包括将子expression式作为单独的函数提取,或者在函数体中放入
case
expression式,以便将某些模式匹配向下推送到主要定义之外。
对我来说,模式匹配和守卫看起来基本相同。 两者都评估一个条件,如果为true,则会执行挂钩的expression式。 我的理解是否正确?
不完全的。 首先模式匹配不能评估任意条件。 它只能检查一个值是否是使用给定的构造函数创build的。
第二个模式匹配可以绑定variables。 所以虽然模式[]
可能等同于guard null lst
(不使用长度,因为它不等于 – 稍后会更多),模式x:xs
当然不等同于guard not (null lst)
因为这个模式绑定了variablesx
和xs
,而守卫则不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链,并使你的代码更具可读性。