You have already seen how to use functions in the ⪆ library,
i.e., how to apply them to arguments.
<P/>
In this section you will see how to write functions in the &GAP; language. You will also see how to use the <K>if</K> statement and declare
local variables with the <K>local</K> statement in the function definition.
Loop constructions via <K>while</K> and <K>for</K> are discussed further, as are
recursive functions.
<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="Writing Functions">
<Heading>Writing Functions</Heading>
<P/>
Writing a function that prints <C>hello, world.</C> on the screen is a simple
exercise in &GAP;.
<P/>
<Example><![CDATA[
gap> sayhello:= function()
> Print("hello, world.\n");
> end;
function( ) ... end
]]></Example>
<P/>
This function when called will only execute the <C>Print</C> statement in the
second line. This will print the string <C>hello, world.</C> on the screen
followed by a newline character <C>\n</C> that causes the &GAP; prompt to
appear on the next line rather than immediately following the printed
characters.
<P/>
The function definition has the following syntax.
<P/>
<K>function</K><C>( <A>arguments</A> ) <A>statements</A></C> <K>end</K>
<P/>
A function definition starts with the keyword <K>function</K> followed by
the formal parameter list <A>arguments</A> enclosed in parenthesis
<C>( )</C>.
The formal parameter list may be empty as in the example. Several
parameters are separated by commas. Note that there must be <E>no</E>
semicolon behind the closing parenthesis. The function definition is
terminated by the keyword <K>end</K>.
<P/>
A &GAP; function is an expression like an integer, a sum or a list.
Therefore it may be assigned to a variable. The terminating semicolon
in the example does not belong to the function definition but
terminates the assignment of the function to the name <C>sayhello</C>.
Unlike in the case of integers, sums, and lists the value of the
function <C>sayhello</C> is echoed in the abbreviated fashion
<C>function( ) ... end</C>.
This shows the most interesting part of a function: its
formal parameter list (which is empty in this example). The complete
value of <C>sayhello</C> is returned if you use the function
<Ref Func="Print" BookName="ref"/>.
<P/>
<Example><![CDATA[
gap> Print(sayhello, "\n");
function ( )
Print( "hello, world.\n" );
return;
end
]]></Example>
<P/>
Note the additional newline character <C>"\n"</C> in the
<Ref Func="Print" BookName="ref"/>
statement. It is printed after the object <C>sayhello</C> to start a new
line. The extra <K>return</K> statement is inserted by &GAP; to simplify
the process of executing the function.
<P/>
The newly defined function <C>sayhello</C> is executed by calling <C>sayhello()</C>
with an empty argument list.
<P/>
<Example><![CDATA[
gap> sayhello();
hello, world.
]]></Example>
<P/>
However, this is not a typical example as no value is returned but only a
string is printed.
In the following example we define a function <C>sign</C> which determines
the sign of an integer.
<P/>
<Example><![CDATA[
gap> sign:= function(n)
> if n < 0 then
> return -1;
> elif n = 0 then
> return 0;
> else
> return 1;
> fi;
> end;
function( n ) ... end
gap> sign(0); sign(-99); sign(11);
0
-1
1
]]></Example>
<P/>
This example also introduces the <K>if</K> statement which is used to execute
statements depending on a condition. The <K>if</K> statement has the
following syntax.
<P/>
<K>if</K> <A>condition</A> <K>then</K>
<A>statements</A>
<K>elif</K> <A>condition</A> <K>then</K>
<A>statements</A>
<K>else</K>
<A>statements</A>
<K>fi</K>
<P/>
There may be several <K>elif</K> parts. The <K>elif</K> part as well as the <K>else</K>
part of the <K>if</K> statement may be omitted. An <K>if</K> statement is no
expression and can therefore not be assigned to a variable. Furthermore
an <K>if</K> statement does not return a value.
<P/>
Fibonacci numbers are defined recursively by <M>f(1) = f(2) = 1</M> and
<M>f(n) = f(n-1) + f(n-2)</M> for <M>n \geq 3</M>.
Since functions in &GAP; may call themselves,
a function <C>fib</C> that computes Fibonacci numbers can be implemented
basically by typing the above equations. (Note however that this is a very
inefficient way to compute <M>f(n)</M>.)
<P/>
<Example><![CDATA[
gap> fib:= function(n)
> if n in [1, 2] then
> return 1;
> else
> return fib(n-1) + fib(n-2);
> fi;
> end;
function( n ) ... end
gap> fib(15);
610
]]></Example>
<P/>
There should be additional tests for the argument <C>n</C> being a positive
integer. This function <C>fib</C> might lead to strange results if called
with other arguments. Try inserting the necessary tests into this example.
A function <C>gcd</C> that computes the greatest common divisor of two
integers by Euclid's algorithm will need a variable in addition to the
formal arguments.
<P/>
<Example><![CDATA[
gap> gcd:= function(a, b)
> local c;
> while b <> 0 do
> c:= b;
> b:= a mod b;
> a:= c;
> od;
> return c;
> end;
function( a, b ) ... end
gap> gcd(30, 63);
3
]]></Example>
<P/>
The additional variable <C>c</C> is declared as a <E>local</E> variable in the
<K>local</K> statement of the function definition. The <K>local</K> statement, if
present, must be the first statement of a function definition. When
several local variables are declared in only one <K>local</K> statement they
are separated by commas.
<P/>
The variable <C>c</C> is indeed a local variable, that is local to the
function <C>gcd</C>. If you try to use the value of <C>c</C> in the main loop you
will see that <C>c</C> has no assigned value unless you have already assigned
a value to the variable <C>c</C> in the main loop. In this case the local
nature of <C>c</C> in the function <C>gcd</C> prevents the value of the <C>c</C> in the
main loop from being overwritten.
<P/>
<Example><![CDATA[
gap> c:= 7;;
gap> gcd(30, 63);
3
gap> c;
7
]]></Example>
<P/>
We say that in a given scope an identifier identifies a unique variable.
A <E>scope</E> is a lexical part of a program text. There is the global scope
that encloses the entire program text, and there are local scopes that
range from the <K>function</K> keyword, denoting the beginning of a function
definition, to the corresponding <K>end</K> keyword. A local scope introduces
new variables, whose identifiers are given in the formal argument list
and the local declaration of the function. The usage of an identifier in
a program text refers to the variable in the innermost scope that has
this identifier as its name.
We have already seen recursion in the function <C>fib</C>
in Section <Ref Sect="If Statements"/>.
Here is another, slightly more complicated example.
<P/>
We will now write a function to determine the number of partitions of
a positive integer. A partition of a positive integer is a descending
list of numbers whose sum is the given integer. For example
<M>[4,2,1,1]</M> is a partition of 8. Note that there is just one partition
of 0, namely <M>[ ]</M>. The complete set of all partitions of an integer
<M>n</M> may be divided into subsets with respect to the largest element.
The number of partitions of <M>n</M> therefore equals the sum of the
numbers of partitions of <M>n-i</M> with elements less than or equal to <M>i</M>
for all possible <M>i</M>. More generally the number of partitions of <M>n</M>
with elements less than <M>m</M> is the sum of the numbers of partitions of
<M>n-i</M> with elements less than <M>i</M> for <M>i</M> less than <M>m</M> and <M>n</M>. This
description yields the following function.
<P/>
<Example><![CDATA[
gap> nrparts:= function(n)
> local np;
> np:= function(n, m)
> local i, res;
> if n = 0 then
> return 1;
> fi;
> res:= 0;
> for i in [1..Minimum(n,m)] do
> res:= res + np(n-i, i);
> od;
> return res;
> end;
> return np(n,n);
> end;
function( n ) ... end
]]></Example>
<P/>
We wanted to write a function that takes one argument. We solved the
problem of determining the number of partitions in terms of a recursive
procedure with two arguments. So we had to write in fact two functions.
The function <C>nrparts</C> that can be used to compute the number of
partitions indeed takes only one argument. The function <C>np</C> takes two
arguments and solves the problem in the indicated way. The only task of
the function <C>nrparts</C> is to call <C>np</C> with two equal arguments.
<P/>
We made <C>np</C> local to <C>nrparts</C>. This illustrates the possibility of
having local functions in &GAP;. It is however not necessary to put
it there. <C>np</C> could as well be defined on the main level, but then
the identifier <C>np</C> would be bound and could not be used for other
purposes, and if it were used the essential function <C>np</C> would no
longer be available for <C>nrparts</C>.
<P/>
Now have a look at the function <C>np</C>. It has two local variables <C>res</C>
and <C>i</C>. The variable <C>res</C> is used to collect the sum and <C>i</C> is a
loop variable. In the loop the function <C>np</C> calls itself again with
other arguments. It would be very disturbing if this call of <C>np</C> was
to use the same <C>i</C> and <C>res</C> as the calling <C>np</C>. Since the new call
of <C>np</C> creates a new scope with new variables this is fortunately not
the case.
<P/>
Note that the formal parameters <A>n</A> and <A>m</A> of <C>np</C> are treated like
local variables.
<P/>
(Regardless of the recursive structure of an algorithm it is often
cheaper (in terms of computing time) to avoid a recursive
implementation if possible (and it is possible in this case), because
a function call is not very cheap.)
</Section>
<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="Further Information about Functions">
<Heading>Further Information about Functions</Heading>
The function syntax is described in Section <Ref Chap="Functions" BookName="ref"/>. The <K>if</K>
statement is described in more detail in Section <Ref Sect="If" BookName="ref"/>. More about
Fibonacci numbers is found in Section <Ref Func="Fibonacci" BookName="ref"/> and more about
partitions in Section <Ref Func="Partitions" BookName="ref"/>.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.