Function definitions are written as a sequence of clauses (or equations) each of which provides the answer for one pattern of arguments:
: or (,..)). A constant only matches itself,
a variable matches anything - and gets bound to the value it matched -
and a constructor only matches values built from that constructor
if the component patterns match.
When a function is applied to an argument the patterns of each clause of the definition are matched, in turn, with the argument. When a match occurs the expression on the right hand side (rhs) is evaluated giving the result of the function. For example:
iszero 0 = True
iszero n = False
if iszero is applied to an argument as in:
iszero 37
it fails to match the first clause because 0 does not equal 37
but the variable ``n'' matches anything so the second clause
is used and False is the result.
The simple function to exchange the components of a pair tuple:
exchange (a,b) = (b,a)
The pattern (a,b) only matches pairs, and a and b
will match any values, they are bound to the two components of the actual
value and then the right hand side reconstructs a pair with the
elements reversed.
The next example is a function called front
that will return the first element of a list 5.3.
Remember that the empty list: [], is a list
so it must be matched but it has no first element:
front [] = error "empty list to front"
front (h:t) = h
the pattern [] will only match an empty list and
the corresponding right hand side has no result, instead
it aborts the program, error is not just an error message.
If the list is non-empty it must have been made
with ``:'' and the pattern (h:t) will
match it binding h to the first element and t to
the rest of the list:
front [1,2,3,4]
is equivalent to:
front (1 : (2 : (3 : (4 : []))))
and the pattern matches setting:
h = 1
t = (2 : (3 : (4 : [])))
This is not about functional programming languages it is about functional programming. It is included because not knowing how to write functions will make it harder to understand the language constructs. Skip this if you know how to write recursive functions.
The only way to define a function is by clauses of patterns and
corresponding answers. So how can there be a non-trivial
function? How can any problem requiring any repeated
operation be solved? Even a problem as simple
as computing the factorial of a number ``n''
would seem, in a procedural language, to require
some repetition: ie.
.
The answer is to think of the solution to the problem in terms of clauses. All computably soluble problems have solutions that can be expressed in cases or patterns of actual arguments. Use the following approach:
Find simple immediate answers for the simple cases, and for all other cases express the answer as some operation on the result of an application of the same function on a simpler argument (ie. one tending towards the simple cases).Using the function to define itself is recursion, if you are not confident about it still do it - it will work - do it as an act of faith, just believe!
To take that trivial problem of the factorial, observe
that the factorial of 0 is defined to be 1,
that's the simple case, and for all other
values 5.4 (other case)
the factorial of any number ``n'' is ``n'' multiplied
by the factorial of the previous number. In Haskell:
factorial 0 = 1
factorial n = n * factorial (n-1)
notice that the second case is an operation on the result
of an application of the function being defined but with
an argument tending towards the simple case, using
factorial to define factorial is the
act of faith.
The previous interlude about thinking of functional language
evaluation in terms of rewriting names by their definitions
can be used to assist understanding recursion:
| factorial 3 | matches the 2nd clause, n=3 and rewrites to: |
| 3 * factorial (3-1) | just replacing factorial 2 rewrite to: |
| 3 * 2 * factorial (2-1) | rewriting factorial 1 by 2nd clause: |
| 3 * 2 * 1 * factorial (1-1) | factorial 0 matches 1st clause and rewrites to 1: |
| 3 * 2 * 1 * 1 | which by arithmetic gives: |
| 6 | done! |
[] not 0 and the simpler argument will be the
tail of the list not a smaller number.
For example to find the length of a list, observe that the
empty list is 0 elements long and all other
lists are one longer than lists one element shorter, so:
len [] = 0
len (h:t) = 1 + len t
Now rewrite len [4,5,6] which is equivalent
to len (4:(5:(6:[])))
| len [4,5,6] | matches
len (h:t)
binding h=4
and t=[5,6]
giving: |
| 1 + len [5,6] | replacing len [5,6] by rhs where h=5 and t=[6] |
| 1 + 1 + len [6] | again with h=6 and t=[] gives: |
| 1 + 1 + 1 + len [] | len[] matches first clause and gives: |
| 1 + 1 + 1 + 0 | which by arithmetic gives: |
| 3 |
takefirst 4 [10,9,8,7,6,5,4]
will give: [10,9,8,7]. Now the function:
takefirst 0 any = []
takefirst n [] = error "list too short"
takefirst n (h:t) = h : takefirst (n-1) t
This is the end of the interlude on recursion, now back
to functions in Haskell.
For most functions simple clauses and patterns are not enough
to select all the different cases for which different answers
are required. For example a function to return the larger
of two numbers cannot be done with patterns alone,
selection of the result depends on a numeric comparison
of the arguments and patterns are only constants, constructors
or variables.
Haskell provides
alternative answers for each pattern's right hand side
depending on the value of a boolean expression called a guard.
otherwise''.
For example to return the larger of two numbers:
larger a b | a>=b = a
| a<b = b
note that a and b match any arguments, the two cases
are selected by evaluating the guards in order until
one gives true and then evaluating the following expression
as the result. Relying on the order and using ``otherwise'':
larger a b | a >= b = a
| otherwise = b
though perhaps it is safer to use mutually exclusive guard conditions.
Multiple pattern clauses and guards can be combined. The following
function isin tests to see whether a list contains a
particular value:
isin [] e = False -- nothing in empty list
isin (h:t) | h == e = True -- is head the goal?
| h /= e = isin t e
Each clause is a separate equation and the bindings of the variables
in the patterns only hold throughout the corresponding right hand side.
The first rule has no bindings for h and t they are
in a different equation but the second equation has two
parts and h and t are bound through both
expressions and both guards.
The right hand side of a clause (just one clause and all its guarded expressions) can have local definitions. These definitions are within the scope of the pattern match for the clause so they can use the argument names and the values they define can be used throughout the guarded expressions. They can be used to avoid writing repeated expressions and to make the code clearer if it is long. The following function uses an auxiliary definition it its third equation:
largest [] = error "no largest in empty list"
largest (h:[]) = h
largest (h:t) | h>l = h
| h<=l = l
where l = largest t
If the where is not used the recursive call, largest t,
to find the largest item in the tail of the list will be
needed by the guard to compare it with the head item and again,
maybe, as the result. The function would be as follows:
largest [] = error "no largest in empty list"
largest (h:[]) = h
largest (h:t) | if h>largest = h
| otherwise = largest t
Note that the otherwise is to avoid using largest t
three times!
© University of Hertfordshire Higher Education Corporation (1998)