Pattern Matching和Guards有什么区别? [英] What is the difference between Pattern Matching and Guards?

查看:156
本文介绍了Pattern Matching和Guards有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Haskell和函数式编程都很新颖。我的问题是非常基本的。
$ b

使用模式匹配的函数



<$>模式匹配和防卫之间有什么区别? p $ p> check :: [a] - > String
check [] =Empty
check(x:xs)=包含元素

使用警卫的功能

  check_ :: [a]  - >字符串
check_lst
|长度lst < 1 =空
|否则=包含元素

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



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



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

解决方案

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

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

模式匹配还有其他一些功能:它们是解构数据的唯一方法,它们在其范围内绑定标识符。在同样的意义上,警卫相当于 if 表达式,模式匹配相当于 case 表达式。声明(无论是在顶层,还是类似于 let 表达式)都是模式匹配的一种形式,正常定义与平凡模式匹配单一标识符。

模式匹配也是Haskell实际发生的主要方式 - 尝试解构模式中的数据是迫使评估。

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

  square =(^ 2)

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

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

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






至于日常问题这里有一些粗略的指南:


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


  • 当您需要根据某些与模式不完全对应的属性进行选择时,绝对使用警卫,例如比较两个 Int 值来查看哪个更大。

  • 如果您只需要几条数据从大型结构的内部深处,特别是如果你还需要将结构作为一个整体来使用,那么守卫和访问函数通常比充满 @ 和<如果你需要为不同模式表示的值做同样的事情,但是用一个方便的谓词来对它们进行分类,使用带有警卫的单一通用模式通常更具可读性。请注意,如果一组警卫不是穷尽的,那么所有警卫都会失败的任何事情都会下降到下一个模式(如果有的话)。因此,您可以将一般模式与一些过滤器结合起来以捕捉特殊情况,然后在其他所有模式中进行模式匹配以获取您关心的细节。 不要用守卫来处理可以用模式轻松检查的事物。检查空列表是一个典型的例子,使用模式匹配。 一般来说,如果有疑问,只需坚持模式匹配默认情况下,它是通常更好。如果一个模式开始变得非常丑陋或令人费解,那就停下来考虑一下如何写它。除了使用警卫之外,其他选项还包括将子表达式作为单独的函数提取,或者在函数体中放置 case 表达式,以便将某些模式匹配推送到它们之外并从主要定义。



I am very new to Haskell and to functional programming in general. My question is pretty basic. What is the difference between Pattern Matching and Guards?

Function using pattern matching

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

Function using guards

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

To me it looks like Pattern Matching and Guards are fundamentally the same. Both evaluate a condition, and if true will execute the expression hooked to it. Am I correct in my understanding?

In this example I can either use pattern matching or guards to arrive at the same result. But something tells me I am missing out on something important here. Can we always replace one with the other?

Could someone give examples where pattern matching is preferred over guards and vice versa?

解决方案

Actually, they're fundamentally quite different! At least in Haskell, at any rate.

Guards are both simpler and more flexible: They're essentially just special syntax that translates to a series of if/then expressions. You can put arbitrary boolean expressions in the guards, but they don't do anything you couldn't do with a regular if.

Pattern matches do several additional things: They're the only way to deconstruct data, and they bind identifiers within their scope. In the same sense that guards are equivalent to if expressions, pattern matching is equivalent to case expressions. Declarations (either at the top level, or in something like a let expression) are also a form of pattern match, with "normal" definitions being matches with the trivial pattern, a single identifier.

Pattern matches also tend to be the main way stuff actually happens in Haskell--attempting to deconstruct data in a pattern is one of the few things that forces evaluation.

By the way, you can actually do pattern matching in top-level declarations:

square = (^2)

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

This is occasionally useful for a group of related definitions.

GHC also provides the ViewPatterns extension which sort of combines both; you can use arbitrary functions in a binding context and then pattern match on the result. This is still just syntactic sugar for the usual stuff, of course.


As for the day-to-day issue of which to use where, here's some rough guides:

  • Definitely use pattern matching for anything that can be matched directly one or two constructors deep, where you don't really care about the compound data as a whole, but do care about most of the structure. The @ syntax lets you bind the overall structure to a variable while also pattern matching on it, but doing too much of that in one pattern can get ugly and unreadable quickly.

  • Definitely use guards when you need to make a choice based on some property that doesn't correspond neatly to a pattern, e.g. comparing two Int values to see which is larger.

  • If you need only a couple pieces of data from deep inside a large structure, particularly if you also need to use the structure as a whole, guards and accessor functions are usually more readable than some monstrous pattern full of @ and _.

  • If you need to do the same thing for values represented by different patterns, but with a convenient predicate to classify them, using a single generic pattern with a guard is usually more readable. Note that if a set of guards is non-exhaustive, anything that fails all the guards will drop down to the next pattern (if any). So you can combine a general pattern with some filter to catch exceptional cases, then do pattern matching on everything else to get details you care about.

  • Definitely don't use guards for things that could be trivially checked with a pattern. Checking for empty lists is the classic example, use a pattern match for that.

  • In general, when in doubt, just stick with pattern matching by default, it's usually nicer. If a pattern starts getting really ugly or convoluted, then stop to consider how else you could write it. Besides using guards, other options include extracting subexpressions as separate functions or putting case expressions inside the function body in order to push some of the pattern matching down onto them and out of the main definition.

这篇关于Pattern Matching和Guards有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆