Programmieren

Funktionen

 aufwärts

Funktionen in der Mathematik

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

Beispiel:  Die Funktion

successor : natürliche Zahlen Pfeil natürliche Zahlen

ordnet einer beliebigen natürlichen Zahl n die nächstgröß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 : natürliche Zahlenkreuznatürliche Zahlen Pfeil natürliche Zahlen

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 C++ und Java

In Programmier­sprachen wie C++ und Java 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 entsprechenden 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ückgegeben. Der Typ des Rückgabewertes wird ganz am Anfang, vor dem Funktionsnamen angegeben, er ist hier ebenfalls int. Der Berechnungs­teil der Funktion wird zwischen geschweifte Klammern eingeschlossen.

Ein Aufruf der Funktion sieht beispielsweise 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ückgegeben 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 mathematischen Definition der Maximum­funktion. Es wäre aber auch möglich gewesen, eine andere Berechnungs­vorschrift anzugeben, etwa die folgende.

double max(double a, double b)
{
    return (a+b+Math.abs(a-b))/2;
}

Wenn auch weniger offensichtlich, wird durch diese Berechnung ebenfalls das Maximum von a und b ermittelt. Hierbei ist Math.abs die in Java enthaltene Absolutbetrag­funktion.

Ein möglicher Aufruf der Funktion ist beispielsweise

x=max(z, 0);

Hierbei wird der Wert der Variablen z, sofern dieser positiv ist, und ansonsten der Wert 0 zurückgegeben 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 zweckmäß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 nichtnegativen 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 haben nur innerhalb der Funktions­definition Gültigkeit.

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.

Beispiel:  In folgendem Programmstück wird eine globale String-Variable trennsymbol deklariert und mit dem Wert ";" belegt. Die nachfolgende Funktion verkette fügt zwei Zeichenreihen 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

Normalerweise 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 erreichbar ist.

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

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 durchzuführen, wird kein Argument benötigt. Es wird jedoch ein Wert vom Typ double zurückgegeben, die Zahl e = 2,718.

Die Berechnung wird nach der Formel

e  =   Summe n = 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 s=1, f=1;
    while (s>1e-14)
    {
        s=s/n;
        f=f+s;
        n=n+1;
    }
    return f;
}

Die Funktion könnte etwa folgendermaßen aufgerufen werden:

double E = e();

Hier wird die Funktion e() aufgerufen und der Funktions­wert 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. Beispielsweise 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 nichtnegative 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 Wertzuweisung, bei der auf der linken Seite des Wertzuweisungs­zeichens 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 Wertzuweisung – beim Aufruf der Funktion erhalten die formalen Parameter die Werte der entsprechenden 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);
    }
    return f;
}

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

Es gibt jedoch eine Klasse 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 Seiteneffekte haben, können sie überhaupt etwas bewirken. Tatsächlich spielen Funktionen, die nur Seiteneffekte haben und keinen Funktions­wert zurückgeben, eine bedeutende Rolle. Als Typ des nicht vorhandenen Rückgabewertes 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;).

 

Rekursive Funktionen

Innerhalb einer Funktions­definition kann ein Aufruf derselben Funktion vorkommen. Eine Funktion kann sich also selbst aufrufen. Man bezeichnet eine solche Funktion als rekursive Funktion.

Beispiel:  Für n Element natürliche Zahlen0 lässt sich die n-te Potenz einer Zahl a induktiv wie folgt definieren:

a n  =   geschweifte Klammer
1    falls n = 0
a · a n-1    sonst

Diese Definition lässt sich unmittelbar in eine rekursive Funktion umsetzen:

double exp(double a, int n)
{
    if (n==0)
        return 1;
    else
        return a*exp(a, n-1);
}

 

Aufgaben

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

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

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

Aufgabe 3:  Setzen Sie folgende induktive Definition der Fakultäts­funktion in eine rekursive Java-Funktion um.

n!  =   geschweifte Klammer
1    falls n = 0
n·(n-1)!    sonst

 

 

Weiter mit:   up del.icio.us digg.com Google Ma.gnolia Mister Wong StumbleUpon YahooMyWeb LinkARENA

 

homeH.W. Lang   FH Flensburg   lang@fh-flensburg.de   Impressum   ©  
Valid HTML 4.01 Transitional