Programmieren

Funktionen

Funktionen in der Mathematik

In der Mathematik ordnet eine Funktion einem Argument einen Funktions­wert zu.

Beispiel:  Die Funktion

successor : ℕ → ℕ

ordnet einer beliebigen natürlichen Zahl n die nächst­größere natürliche Zahl n+1 zu:

successor(n)  =  n+1

Hierbei ist n das Argument und n+1 der zugehörige Funktions­wert.

 

Die Funktion

max : ℕ × ℕ → ℕ

ordnet einem beliebigen Paar von natürlichen Zahlen (a, b) die größere dieser beiden Zahlen zu.

max(a, b)  =   geschweifte Klammer
a    falls a > b
b    sonst

Hierbei ist das Paar (a, b) das Argument und das Maximum von a und b der zugehörige Funktions­wert.

Definition und Aufruf von Funktionen in Java

In Programmier­sprachen wird der Begriff der Funktion in ähnlicher Weise benutzt.

Auch eine Java-Funktion kann einem Argument einen Funktions­wert zuordnen. In der Funktions­definition wird dabei zunächst allgemein angegeben, wie der Funktions­wert aus dem Argument zu berechnen ist. Beim Funktions­aufruf erhält die Funktion dann einen konkreten Argumentwert, führt die Berechnung durch und gibt den ent­sprechenden Funktions­wert zurück.

Beispiel:  Die Definition der Funktion successor aus dem obigen Beispiel lässt sich in Java wie folgt realisieren:

int successor(int n)
{
    return n+1;
}

Der Name der Funktion ist successor, als Argument erhält die Funktion eine int-Zahl n, und mit der Anweisung return wird der berechnete Funktions­wert zurück­gegeben. Der Typ des Rückgabe­wertes wird ganz am Anfang, vor dem Funktions­namen angegeben, er ist hier ebenfalls int. Der Berechnungs­teil der Funktion wird zwischen geschweifte Klammern ein­geschlossen.

Ein Aufruf der Funktion sieht beispiels­weise wie folgt aus:

k=successor(17);

Als Argumentwert wird hier für n der Wert 17 eingesetzt; als Ergebnis des Funktions­aufrufs wird der Wert 18 zurück­gegeben und der Variablen k zugewiesen.

 

Beispiel:  Die Definition der Funktion max lässt sich in Java wie folgt realisieren:

double max(double a, double b)
{
    if (a>b)
        return a;
    else
        return b;
}

Diese Funktions­definition orientiert sich genau an der mathe­matischen Definition der Maximum­funktion.

Ein möglicher Aufruf der Funktion ist beispiels­weise

x=max(z, 0);

Hierbei wird der Wert der Variablen z, sofern dieser positiv ist, und ansonsten der Wert 0 zurück­gegeben und der Variablen x zugewiesen.

Globale und lokale Variablen

Variablen dienen zur Speicherung von Werten, z.B. von Zwischen­ergebnissen von Berechnungen. Fallen Zwischen­ergebnisse bei einer Berechnung in einer Funktions­definition an, so werden diese zweck­mäßiger­weise in lokalen Variablen gespeichert. Lokale Variablen werden innerhalb der Funktions­definition deklariert. Sie haben nur innerhalb der Funktions­definition Gültigkeit, von außerhalb sind sie nicht zugänglich.

Beispiel:  Die Funktion exp2(n) ordnet einer nicht­negativen ganzen Zahl n als Funktions­wert die Zweierpotenz 2n zu. Mit folgender Funktion lässt sich die Berechnung durchführen:

int exp2(int n)
{
    int i=1;
    int f=1;
    while (i<=n)
    {
        f=f*2;
        i=i+1;
    }
    return f;
}

Hierbei sind i und f lokale Variablen, diese sind nur innerhalb der Funktions­definition gültig.

Im Gegensatz zu lokalen Variablen stehen globale Variablen. Globale Variablen werden außerhalb der Funktions­definition deklariert. Sie haben außerhalb und innerhalb der Funktions­definition Gültigkeit. In Java sind die Attribute der Klasse, in der die Funktion definiert ist, globale Variablen.

Beispiel:  In folgendem Programm­stück wird eine globale String-Variable trennsymbol deklariert und mit dem Wert ";" belegt. Die nachfolgende Funktion verkette fügt zwei Zeichen­reihen unter Einschluss des Trennsymbols zusammen.

String trennsymbol=";";

String verkette(String s, String t)
{
    return s+trennsymbol+t;
}

Ein Aufruf der Funktion, etwa mit s=verkette("alpha", "beta");, liefert als Funktions­wert das Ergebnis "alpha;beta"; dieser wird hier der Variablen s zugewiesen.

Namens­konflikte

Normaler­weise haben innerhalb einer Funktion sowohl die dort deklarierten lokalen Variablen als auch die außerhalb der Funktion deklarierten globalen Variablen Gültigkeit. Ein Problem tritt auf, wenn eine globale Variable und eine lokale Variable denselben Namen haben. Wenn also z.B. beide Variablen x heißen und wenn dann innerhalb der Funktion auf x zugegriffen wird, dann ist zunächst unklar, welches x gemeint ist. Daher gilt die Regel, dass innerhalb der Funktion die lokale Variable Vorrang hat. Die Konsequenz ist, dass innerhalb der Funktion die globale Variable gleichen Namens nicht ohne Weiteres erreichbar ist. Man sagt, die lokale Variable verdeckt die globale Variable.

Bemerkung:  In Java lässt sich auf globale Variablen, die ja Attribute der Klasse sind, durch Voranstellen des Schlüsselworts this dennoch zugreifen, auch wenn sie durch lokale Variablen verdeckt sind (bzw. durch Voranstellen des Namens der Klasse bei Klassen­attributen).

Beispiel:  Beim Aufruf der im Folgenden definierten Version der Funktion verkette, etwa mit verkette("alpha", "beta");, wird das Ergebnis "alpha+beta" zurück­gegeben.

String trennsymbol=";";

String verkette(String s, String t)
{
    String trennsymbol="+";
    return s+trennsymbol+t;
}

Es werden zwei Variablen deklariert, eine globale Variable trennsymbol und eine lokale Variable mit demselben Namen. Die globale Variable trennsymbol erhält den Wert ";", die lokale Variable trennsymbol erhält den Wert "+". Innerhalb der Funktion hat die lokale Variable trennsymbol Vorrang. Immer, wenn innerhalb der Funktion auf die Variable trennsymbol zugegriffen wird, ist die lokale Variable trennsymbol gemeint, und diese hat den Wert "+".

Funktionen ohne Argumente

In Programmier­sprachen gibt es auch Funktionen, die zur Berechnung des Funktions­wertes keinen Argumentwert benötigen.

Beispiel:  Die folgende Funktion berechnet die eulersche Zahl e. Um die Berechnung durch­zuführen, wird kein Argument benötigt. Es wird jedoch ein Wert vom Typ double zurück­gegeben, die Zahl e = 2,718.

Die Berechnung wird nach der Formel

e  =   Summen = 0, ..., unendlich  1 / n!

durchgeführt. Da nicht unendlich viele Summanden berechnet werden können, wird nur solange gerechnet, wie die Summanden größer als 10-14 sind, dann wird die Berechnung abgebrochen, da eine ausreichende Genauigkeit erreicht ist.

double e()
{
    int n=1;
    double f=1, s=1;
    while (f>1e-14)
    {
        f=f/n;
        s=s+f;
        n=n+1;
    }
    return s;
}

Die Funktion wird beispiels­weise wie folgt aufgerufen:

double E = e();

Der zurück­gegebene Funktions­wert wird der Variablen E zugewiesen.

Parameterübergabe

Im Bereich der Programmier­sprachen wird bei Funktionen statt des Begriffs "Argument" meist der Begriff "Parameter" verwendet. Funktionen können einen oder mehrere oder auch gar keinen Parameter haben. Beispiels­weise hat die Funktion successor(n) einen Parameter, die Funktion max(a, b) hat zwei Parameter, und die Funktion e() hat keinen Parameter.

In der Funktions­definition werden die Parameter als formale Parameter bezeichnet, beim Funktions­aufruf werden die Parameter als aktuelle Parameter bezeichnet.

Beispiel:  Die folgende Funktion exp berechnet an für eine double-Zahl a und eine nicht­negative int-Zahl n.

double exp(double a, int n)
{
    int i;
    double p=1;
    for (i=0; i<n; i++)
        p=p*a;
    return p;
}

In dieser Funktions­definition sind a und n die formalen Parameter.

Formale Parameter spielen im Prinzip dieselbe Rolle wie lokale Variablen. Ebenso wie lokale Variablen haben sie nur innerhalb der Funktions­definition Gültigkeit, und bei Namens­konflikten mit globalen Variablen haben sie Vorrang.

Der Unterschied zu gewöhnlichen lokalen Variablen besteht darin, dass die formalen Parameter beim Aufruf der Funktion mit den Werten der aktuellen Parameter initialisiert werden. Dabei kommt es auf die Reihenfolge der Parameter an: Der erste formale Parameter erhält den Wert des ersten aktuellen Parameters, der zweite formale Parameter erhält den Wert des zweiten aktuellen Parameters usw. Dieser Vorgang wird als Parameter­übergabe bezeichnet.

Beispiel:  Beim Aufruf der Funktion exp, etwa durch

double z=exp(2, 10);

erhält der formale Parameter a den Wert 2 und der formale Parmeter n den Wert 10. Der Funktions­aufruf ergibt also hier den Wert 210  =  1024.

Die aktuellen Parameter müssen also immer Werte sein bzw. Ausdrücke, die Werte ergeben. Die formalen Parameter müssen dagegen immer Variablen sein.

Die Situation ist genau wie bei einer Wert­zuweisung, bei der auf der linken Seite des Wert­zuweisungszeichens immer eine Variable stehen muss und auf der rechten Seite immer ein Ausdruck, der einen Wert ergibt. Tatsächlich handelt es sich bei der Parameter­übergabe um eine Wert­zuweisung – beim Aufruf der Funktion erhalten die formalen Parameter die Werte der ent­sprechenden aktuellen Parameter.

Seiteneffekte

Eigentlich erwartet man von einer Funktion, dass sie beim Aufruf den Funktions­wert berechnet und sonst nichts tut. Tatsächlich jedoch können in der Funktions­definiton noch weitere Anweisungen enthalten sein, die mit der eigentlichen Berechnung des Funktions­werts nichts zu tun haben.

Beispiel:  In der Funktion zur Berechnung von e gibt eine Ausgabe­anweisung in jeder Iteration die erreichte Annäherung an den schließlich berechneten Funktions­wert aus.

double e()
{
    int n=1;
    double s=1, f=1;
    while (s>1e-14)
    {
        s=s/n;
        f=f+s;
        n=n+1;
        System.out.println(f);   // Seiteneffekt
    }
    return f;
}

Normaler­weise sollten Funktionen frei von Seiten­effekten sein. Der Grund ist, dass man nicht erwartet, dass außer der Berechnung des Funktions­wertes noch andere Dinge geschehen.

Es gibt jedoch eine bestimmte Art von Funktionen, für die dies nicht gilt. Dies sind Funktionen ohne Rückgabewert.

Funktionen ohne Rückgabewert

Funktionen, die keinen Funktions­wert berechnen, sind eigentlich sinnlos. Nur wenn sie Seiten­effekte haben, können sie überhaupt etwas bewirken. Tatsächlich spielen Funktionen, die nur Seiten­effekte haben und keinen Funktions­wert zurückgeben, eine bedeutende Rolle. Als Typ des nicht vorhandenen Rückgabe­wertes wird bei einer solchen Funktion das Wort void eingesetzt (engl.: void – leer, nichtig).

Beispiel:  Die folgende Funktion weist der globalen Variablen trennsymbol einen neuen Wert zu.

String trennsymbol=";"

void neuesTrennsymbol(String t)
{
    trennsymbol=t;
}

Die Funktion gibt keinen Funktions­wert zurück. Als Seiteneffekt verändert sie jedoch den Wert der globalen Variablen trennsymbol. Nach Aufruf etwa von

neuesTrennsymbol("/");

hat die globale Variable trennsymbol den neuen Wert "/".

Funktionen ohne Rückgabewert werden wie Anweisungen aufgerufen (etwa: neuesTrennsymbol("/");); dagegen werden Funktionen, die einen Rückgabewert erzeugen, in der Regel innerhalb von Ausdrücken aufgerufen (etwa: x=2*max(a, b)+1;).

Aufgaben

Aufgabe 1:  Schreiben Sie eine Funktion square(x), die das Quadrat einer Zahl x berechnet.

Aufgabe 2:  Setzen Sie folgende mathe­matische Funktions­definition in eine Java-Funktion um.

abs(x)  =   geschweifte Klammer
-x    falls x < 0
x    sonst

 

Weiter mit:  [Funktionen (Fortsetzung)]   [Literatur]   oder   [up]

 


H.W. Lang   mail@hwlang.de   Impressum   Datenschutz
Diese Webseiten sind während meiner Lehrtätigkeit an der Hochschule Flensburg entstanden