Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it better to use guards than patterns for recursion functions in Haskell?

I'm just wondering about a recursion function I'm laying out in Haskell. Is it generally better to use guards than patterns for recursion functions?

I'm just not sure on what the best layout is but I do know that patterns are better when defining functions such as this:

units :: Int -> String

units 0 = "zero"
units 1 = "one"

is much preferred to

units n
    | n == 0 = "zero"
    | n == 1 = "one"

I'm just not sure though when it comes to recursion as to whether this is the same or different.

Just not quite sure on terminology: I'm using something like this:

f y [] = [] 
f y (x:xs) 
    | y == 0 = ...... 
    | otherwise = ...... 

or would this be better?

f y [] = [] 
f 0 (x:xs) = 
f y (x:xs) =
like image 488
maclunian Avatar asked May 20 '11 20:05

maclunian


People also ask

When should I use guards in Haskell?

Haskell guards are used to test the properties of an expression; it might look like an if-else statement from a beginner's view, but they function very differently. Haskell guards can be simpler and easier to read than pattern matching .

How does Haskell optimize recursion?

Recursive functions are more practical in Haskell than in imperative languages, due to referential transparency and laziness. Referential transparency allows the compiler to optimize the recursion away into a tight inner loop, and laziness means that we don't have to evaluate the whole recursive expression at once.

Can you pattern match in guards Haskell?

The PatternGuards extension, now officially incorporated into the Haskell 2010 language, expands guards to allow arbitrary pattern matching and condition chaining. The existing syntax for guards then becomes a special case of the new, much more general form. You start a guard in the same way as always, with a | .


1 Answers

My general rule of thumb would be this:

  • Use pattern matching when the guard would be a simple == check.

With recursion, you usually are checking for a base case. So if your base case is a simple == check, then use pattern matching.

So I'd generally do this:

map f [] = []
map f (x:xs) = f x : map f xs

Instead of this (null simply checks if a list is empty. It's basically == []):

map f xs | null xs   = []
         | otherwise = f (head xs) : map f (tail xs)

Pattern matching is meant to make your life easier, imho, so in the end you should do what makes sense to you. If you work with a group, then do what makes sense to the group.

[update]

For your particular case, I'd do something like this:

f _ []      = []
f 0 _       = ...
f y (x:xs)  = ...

Pattern matches, like guards, fall from top to bottom, stopping at the first definition that matches the input. I used the underscore symbol to indicate that for the first pattern match, I didn't care what the y argument was, and for the second pattern match, I didn't care what the list argument was (although, if you do use the list in that computation, then you should not use the underscore). Since it's still fairly simple ==-like checks, I'd personally stick with pattern matching.

But I think it's a matter of personal preference; your code is perfectly readable and correct as it is. If I'm not mistaken, when the code is compiled, both guards and pattern matches get turned into case statements in the end.

like image 145
Dan Burton Avatar answered Nov 16 '22 02:11

Dan Burton