Pattern Matching和Guards有什么区别?

Var*_*jan 61 syntax haskell guard

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

使用模式匹配的功能

check :: [a] -> String
check [] = "Empty"
check (x:xs) = "Contains Elements"
Run Code Online (Sandbox Code Playgroud)

使用警卫的功能

check_ :: [a] -> String
check_ lst
    | length lst < 1 = "Empty"
    | otherwise = "Contains elements"
Run Code Online (Sandbox Code Playgroud)

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

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

有人可以提供一些例子,其中模式匹配比警卫更受欢迎,反之亦然吗?

C. *_*ann 60

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

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

模式匹配还有几个额外的东西:它们是解构数据唯一方法,它们在其范围内绑定标识符.在保护等同于if表达式的同一意义上,模式匹配等同于case表达式.声明(在顶层,或类似let表达式)也是模式匹配的一种形式,"普通"定义与普通模式匹配,即单个标识符.

模式匹配也往往是Haskell中实际发生的主要方式 - 尝试以模式解构数据是迫使评估的少数因素之一.

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

square = (^2)

(one:four:nine:_) = map square [1..]
Run Code Online (Sandbox Code Playgroud)

这偶尔对一组相关定义有用.

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


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

  • 绝对使用模式匹配可以直接匹配一个或两个构造函数的任何东西,在那里你并不真正关心整个复合数据,但是要关心大部分结构.该@语法可以让你的整体结构绑定到一个变量,同时图案也匹配,但这样做在一个模式太多,可以得到丑陋,无法读取快.

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

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

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

  • 绝对不要使用警卫来处理可以用模式轻易检查的东西.检查空列表是典型示例,使用模式匹配.

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

  • 别忘了他们的宝贝,图案卫士! (5认同)

sep*_*p2k 10

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

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

第二种模式匹配可以绑定变量.因此,尽管该模式[]可能是等同于保护null lst(不使用长度,因为那会不等于-后来更多),该模式x:xs肯定是不等同于后卫not (null lst)因为该模式结合的变量xxs,其中保护不不.

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


Jus*_*ier 9

首先,您可以将布尔表达式放在一个保护中.

例如:

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

f x | [y] <- x
    , y > 3
    , Just z <- h y
    = ...
Run Code Online (Sandbox Code Playgroud)


更新

Learn You a Haskell有一个很好的引用:

模式是确保值符合某种形式并解构它的一种方式,而防护是一种测试某个值(或其中几个)的属性是真还是假的方法.这听起来很像if语句,而且非常相似.事情是,当你有几个条件时,守卫会更具可读性,并且他们可以很好地使用模式.

  • @Antal,实际上它是标准的Haskell 2010. http://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-460003.13 (3认同)
  • 另一方面,到目前为止,最流行的编译器中的纯语法扩展是非常安全的.最糟糕的情况是,为了移植到其他编译器,您必须稍后手动去除它. (2认同)

I G*_*ERS 5

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

f y = ...
f x =
  if p(x) then A else B
Run Code Online (Sandbox Code Playgroud)

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

f y = ...
f x | p(x) = A
    | otherwise = B
Run Code Online (Sandbox Code Playgroud)

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

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