What is the difference between pattern matching and guards?

I am very new to Haskell and to functional programming in general. My question is pretty simple. What is the difference between matching patterns and guards?

Feature Using Pattern Matching

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

Function with protection

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

It seems to me that Pattern Matching and Guard are fundamentally the same. Both evaluate the condition, and if true executes the expression associated with it. Am I right in my understanding?

In this example, I can either use pattern matching or guards to get the same result. But something tells me that I'm missing something important here. Can we always replace it with another?

Can someone give examples where pattern matching is preferable to protection and vice versa?

+51
syntax haskell guard
Nov 11 2018-10-11
source share
4 answers

Actually, they are fundamentally different! At least in Haskell, anyway.

Guards are simpler and more flexible: they are essentially just special syntax that translates into a series of if / then expressions. You can put arbitrary Boolean expressions into guards, but they do nothing that you could not do with regular if .

Pattern matches perform several additional actions: they are the only way to deconstruct data , and they bind identifiers within their area . In the same sense that guards are equivalent to if , pattern matching is equivalent to case . Declarations (either at the top level or in the form of a let expression) are also a form of template matching, while the “usual” definitions coincide with a trivial template, one identifier.

Comparison of templates is also usually the main way that Haskell actually does - trying to deconstruct data in a template is one of the few things that makes you appreciate.

By the way, you can actually match patterns in top-level ads:

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

This is sometimes useful for a group of related definitions.

The GHC also provides a ViewPatterns extension that combines both; you can use arbitrary functions in the context of the binding, and then match the result by result. Of course, it's still just syntactic sugar for ordinary things.




Regarding the daily issue, due to which you can use somewhere a few rough guides:

  • Definitely use pattern matching for anything that can be paired directly with one or two constructors in depth, where you really don't need complex data in general, but take care of most of the structure. The @ syntax allows you to bind the general structure to a variable, as well as matching patterns on it, but there is too much of what can be ugly and unreadable in one template quickly.

  • Definitely use protective equipment when you need to make a choice based on some property that does not fit neatly into the template, for example. comparing two Int values ​​to see which is larger.

  • If you need only a few pieces of data from the depths of a large structure, especially if you also need to use the structure as a whole, the security and access functions are generally more readable than some monstrous template full of @ and _ .

  • If you need to do the same for values ​​represented by different patterns, but with a convenient predicate for classifying them, using one common pattern with protection is usually more readable. Please note that if the set of guards is not exhaustive, everything that does not work, all defenders will fall to the next pattern (if any). That way, you can combine a common template with some kind of filter to catch exceptional cases, and then match the templates in everything else to get the information you care about.

  • Definitely do not use protective devices for things that can be trivially checked with a pattern. Checking empty lists is a classic example; use a matching pattern for this.

  • In general, if in doubt, just stick to the default template, it is usually nicer. If the template starts to get really ugly or confusing, stop to consider how else you could write it. In addition to using protective devices, other options include extracting subexpressions as separate functions or placing case expressions inside the function body to push some of the templates to them and from the main definition.

+52
Nov 11 2018-10-11
source share

It seems to me that Pattern Matching and Guard are fundamentally the same. Both evaluate the condition, and if true executes the expression associated with it. Am I right in my understanding?

Not really. The first match of patterns cannot evaluate arbitrary conditions. It can only check if the value was created using this constructor.

The second pattern matching can bind variables. Therefore, although the pattern [] may be equivalent to the null lst protector (not using length because it would not be equivalent - more on that later), the x:xs pattern is certainly not equivalent to the not (null lst) guard because the pattern binds x and xs variables that are not protected.

A note on using length : using length to check if a list is empty is a very bad practice, because to calculate the length you need to go through the whole list, which takes O(n) and just checks if it is empty, takes O(1) time with null or pattern matching. Further use of `length 'just doesn't work in infinite lists.

+10
Nov 11 2018-10-11
source share

First, you can put logical expressions in a protective shell.

For example :

As with lists, Boolean expressions can be freely mixed between security elements of templates. For example:

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




<b> Update

There is a good quote from Learn You a Haskell about the difference:

While patterns are a way to make sure that a value matches some form and deconstructs it, advocates are a way to check if any property of a value (or several of them) is true or false. This is very similar to the if statement, and it is very similar. The fact is that guards are much more readable when you have several conditions, and they play very well with patterns.

+9
Nov 11 2018-10-11
source share

In addition to other good answers, I’ll try to be specific about the guards: the guards are just syntactic sugar. If you think about it, you will often have the following structure in your programs:

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

That is, if the pattern matches, it follows immediately if-then-else discrimination. Advocate reduces this discrimination directly to a pattern:

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

( otherwise defined as True in the standard library). It is more convenient than the if-then-else chain, and sometimes it also makes the code much simpler, which means it's easier to write than the if-then-else construct.

In other words, it is sugar on top of another design in a way that in many cases greatly simplifies your code. You will find that it removes many if-then-else chains and makes your code more readable.

+5
Nov 11 2018-10-11
source share



All Articles