6. Grundlagen von Funktionen - fbi.h

Werbung
6. Grundlagen von Funktionen
C++ Programm
Bestandteile eines C++ - Programms
C++ Kern
vordefinierte Datentypen, Operatoren, Befehle
Standardbibliothek
vordefinierte Funktionen (und Klassen)
Anwender
Selbsterstellte Funktionen (und Klassen)
Sonstiges
externe Bibliotheken
Dr. Norbert Spangler /Informatik
21.11.2012
2
Beispiele für mathematische Funktionen
while ( abs (x*x-a) >=1.0e-10 );
-> abs
x1= sqrt( b*b - 4*a*c);
-> sqrt
y = cos(x*pi);
-> cos
usw.
Diese mathematischen Funktionen werden neben zahlreichen
anderen in der Bibliothek cmath zur Verfügung gestellt.
Erforderlich #include <cmath>
Dr. Norbert Spangler /Informatik
(vgl. Musterprogramm)
21.11.2012
3
Beispiele für andere Funktionen
Suchen eines Elements in einem Array
erforderlich: Arrayelemente + Anzahl +Suchelement
Ergebnis=Funktionswert: gefundener Index (Stelle)
stelle = suche(array,anzahl,suchelement)
Sortieren der Elemente eines Arrays
erforderlich: Arrayelemente + Anzahl
Funktionswert ?
Ergebnis: sortierter Array
sortiere(array,anzahl)
Formatierte Ausgabe eines Arrays (einer Matrix) am Bildschirm
erforderlich: Arrayelemente + Anzahl
Funktionswert ?
Ergebnis: Bildschirmanzeige
anzeige(array,anzahl)
Regel: Funktionen machen keine Ausgaben, es sei denn es handelt sich um eine
Ausgabefunktion
Dr. Norbert Spangler /Informatik
21.11.2012
4
Motivation
Funktionen lösen spezielle (Teil-)Aufgaben
mathematische Formeln
spezielle Algorithmen: Suchen, Sortieren, Gleichungen, Auswertungen
Ein-/Ausgabe für Tastatur, Bildschirm, Dateien usw.
Steuerungen von Geräten, Anlagen,…
Funktionen sind damit eine Zusammenfassung von Arbeitspaketen und bieten
eine Möglichkeit zur Verteilung des Programmieraufwands auf
Teams
Voraussetzung
Die Schnittstellen der Funktionen untereinander sind klar festgelegt
bzw. beschrieben.
Dr. Norbert Spangler /Programmieren
21.11.2012
5
Motivation
Funktionen sind damit "selbstständige" Programmteile innerhalb einer
Anwendung zur
-Strukturierung/Beherrschung der Komplexität (Komponenten)
- Verbesserung des Testens
- Lösung von Teilaufgaben
- Verbesserung der Übersichtlichkeit und Lesbarkeit
- Wiederverwendung (z. Bsp. Ein-/Ausgabe von Arrays)
- Arbeitsorganisation d.h. Aufteilung auf Teams
- Übersetzung: jede Funktion wird durch einen eigenen Compilerlauf bearbeitet
Damit könnten auch unterschiedliche Programmiersprachen verwendet werden
- Archivierung in Bibliotheken
Funktionen erhöhen die Programmqualität
Dr. Norbert Spangler /Programmieren
21.11.2012
6
Beispiel / Musterprogramm
Wurzel berechnen
Wurzel berechnen
Eingabe a
Eingabe a
a<=0
a <= 0
Ja
a muss >0
sein
ja
Nein
nein
a muss > 0 sein
Ausgabe wurzel(a)
x=a
Solangex*x-a ungenau
x*x-a
x = x - ------2x
Wurzel(a)
x=a
Ausgabe x
Solangex*x-a ungenau
Das große "unübersichtliche" Struktogramm
wird ersetzt durch mehrere "kleine"
übersichtliche für jede Funktion
- Eingabe mit Plausibilitätskontrolle
- „eigentliche“ Aufgabe
Dr. Norbert Spangler /Informatik
x*x-a
x = x - ------2x
Ergebnis x
21.11.2012
7
main mit Verwendung einer Funktion wurzel
// Wurzel berechnen
#include <iostream>
using namespace std;
void main()
{
double radikand;
double wurzel(double);
cout <<"\n radikand eingeben ";
cin>>radikand;
//Plausibilitaetskontrolle
if ( radikand<0 )// unzulaessige Eingabe
cout <<"\n darf nicht < 0 sein ";
else
//Ergebnis
cout <<"\n Wurzel von "<<radikand
<<" = "<< wurzel(radikand)
<<endl;
}
Dr. Norbert Spangler /Informatik
Funktion1: main
Prototyp
Dadurch ist wurzel in main bekannt
Aufruf
hier wird wurzel verwendet
aktuelle Parameter
mit diesen Daten aus dem main soll
wurzel arbeiten
21.11.2012
8
Die Funktion wurzel
formale Parameter (Platzhalter)
double wurzel(double a)
{
const double genauigkeit=0.001;
double x=a;
if (a==0.0)
return 0.0;
else
while ( abs (x*x-a)>genauigkeit )
x=x-(x*x-a)/(2*x);
return x;
}
Funktionskopf
Definition
Funktionsblock
Ergebnis (Funktionswert)
Dr. Norbert Spangler /Informatik
21.11.2012
9
Dateien
Üblicherweise sollte man jede Funktion innerhalb eines
Projekts in einer eigenen Datei speichern.
Anmerkung: es geht auch mit einer Datei.
main
Funktion
in Datei main.cpp
in einer Datei func.cpp
Compiler erzeugt main.obj
Compiler erzeugt func.obj
Linker
projekt.exe-Datei
Dr. Norbert Spangler /Informatik
21.11.2012
10
Verallgemeinerung
Situation:
main (ist eine benutzerdefinierte Funktion); sie verwendet
wurzel (ist eine weitere benutzerdefinierte Funktion), diese verwendet
abs (ist eine bereitgestellte Funktion)
Allgemein:
Eine Funktion1 verwendet eine andere Funktion2
Dr. Norbert Spangler /Informatik
21.11.2012
11
6.1 Aufgabenstellung/Anforderungen
funktion1 verwendet funktion2
z.B. funktion1 geschrieben von Programmierer a
funktion2 geschrieben von Programmierer b
Wie erkennt funktion1 dass es funktion2 gibt?
Was passiert wenn funktion2 nicht zur Verfügung steht?
Wie kann funktion1 weiter programmiert werden wenn funktion2 noch nicht
verfügbar ist?
Kann funktion2 programmiert/getestet werden wenn funktion1 noch nicht
existiert?
Wie erfährt funktion2, mit welchen Daten aus funktion1 sie arbeiten muss?
Wie wird das Ergebnis von funktion2 an funktion1 übermittelt?
Bei Mehrfachverwendung von funktion2 in funktion1:
An welcher Stelle in funktion1 geht es weiter?
Dr. Norbert Spangler /Informatik
21.11.2012
12
Schnittstellen funktion1(main) – funktion2 (f)
main
f(t)
y=f(x);
Programmcode
Programmcode
Sprung
Sprung
Rücksprung Stelle 1
z=f(h-2)+1;
Rücksprung Stelle 2
x
y
h
z
Variante 2: Adresse verraten
t
Daten
Daten
return
Variante 1: Wert ausrechnen kopieren
Dr. Norbert Spangler /Programmieren
21.11.2012
13
Erfüllung der Anforderungen/1
Wie erkennt funktion1 dass es funktion2 gibt?
Ein Hinweis ist erforderlich: Prototyp
Was passiert wenn funktion2 nicht zur Verfügung steht?
Fehlermeldung des Linkers
Wie kann funktion1 programmiert werden wenn funktion2 nicht
verfügbar ist?
Man erstellt eine einfache Vorabversion von funktion2 ohne
wesentliche Funktionalität: Stub
Kann funktion2 programmiert/getestet werden wenn funktion1 nicht
existiert?
Man erstellt ein separates Testprogramm für funktion2: Treiber
Dr. Norbert Spangler /Programmieren
21.11.2012
14
Erfüllung der Anforderungen/2
Wie erfährt funktion2, mit welchen Daten aus funktion1 sie arbeiten muss?
Bei der Verwendung der Funktion (Aufruf) müssen diese übermittelt werden.
Hierzu gibt es unterschiedliche Techniken (Kopien/Adressen).
Wie wird das Ergebnis von funktion2 an funktion1 übermittelt?
Hierzu gibt es unterschiedliche Techniken.
Die einfachste Variante ist mittels des Returnbefehls: return Funktionswert;
Bei Mehrfachverwendung von Funktion2:
An welcher Stelle in funktion1 geht es weiter?
Die Stelle muss beim Wechsel vom Programmcode von funktion1
in den von funktion2 gemerkt und bei der Rückkehr verwendet werden.
Dies wird automatisch erledigt.
Dr. Norbert Spangler /Informatik
21.11.2012
15
Weitere Aspekte
Eine Funktion hat einen Wert (Typ)
Prinzipiell ist jeder Typ zulässig, also auch int, short,char,….
Hat eine Funktion "keinen Wert" wird als Typ void verwendet.
Funktionen vom Typ void werden direkt aufgerufen
Beispiel : tausche(x,y);
sortiere(n,x);
Funktionen mit Typ erscheinen als R-Wert
Beispiel: w=wurzel(radikand) ;
cout<<wurzel(b*b-4*a*c) <<endl;
Eine Funktion hat kann beliebig viele Parameter haben
Parameter werden durch Komma getrennt
Beispiel wurzel(a,genauigkeit)
allgemein funktionsname(parameter1,parameter2,.....,parameter_n)
aber auch void main() //ohne Parameter
Dr. Norbert Spangler /Informatik
21.11.2012
16
6.2 Programmierung von Funktionen
Die Funktion sollte sinnvollerweise in einer eigenen cpp-Datei im Projekt
stehen. Daher ist zu main eine weitere cpp-Datei hinzuzufügen.
Der Name kann beliebig gewählt werden, üblicherweise
Funktionsname.cpp.
Diese enthält dann die
Definition der Funktion.
Je nach Größe des Projekts bzw. Anzahl oder Umfang der Funktionen kann
man auch mehrere Funktionen zu einer Funktionsgruppe in einer Datei
zusammenfassen.
Ziel sollte generell die Übersichtlichkeit des Gesamtprogramms sein.
Dr. Norbert Spangler /Informatik
21.11.2012
17
Funktionsdefinition
Angabe von Funktionskopf
Typ der Funktion
und
entspricht dem Typ des return-Wertes
Name der Funktion
wie ein Variablenname
Deklarationsliste
Typ und Name der Parameter
genauer: formale Parameter (Platzhalter)
Funktionsblock
{ hier steht was die Funktion macht }
im Funktionsblock wird der Wert der Funktion
ermittelt und durch return wert;
beendet und dem aufrufenden Programm das
Ergebnis (wert) dann übergeben
Ist die Funktion vom Typ void, so wird sie durch
return; beendet.
Dr. Norbert Spangler /Informatik
21.11.2012
18
Definition/Beispiel
#include<cmath>
using namespace std;
double wurzel(double a)
{
const double genauigkeit=0.001;
double x=a;
if (a==0.0)
return 0.0;
else
while ( abs (x*x-a)>genauigkeit )
x=x-(x*x-a)/(2*x);
return x;
}
Dr. Norbert Spangler /Informatik
Wird hier benötigt wegen abs
Achtung:
Alle hier vorkommenden Größen sind
lokale Größen der Funktion
a
genauigkeit
x
Ist die Funktion vom Typ void darf
mittels return kein Wert zurückgegeben
werden (also nur return; ).
21.11.2012
19
Funktionskopf
Mit dem Funktionskopf beginnt die Definition der Funktion.
Anzugeben sind
Typ des Ergebnisses der Funktion
Name der Funktion (wie Variablennamen)
Liste der formalen Parameter in Klammern()
Parametertyp1 Parametername1,Parametertyp2 Parametername2,….
typfunktion name(typ1 p1,…,typn pn)
Beispiele:
double wurzel(double a) //1 Parameter
double wurzel(double a, double genauigkeit) //2 Parameter
double loesung1(double a, double b, double c) //quadratische Gleichung - 3 Par.
int zufallszahl() //kein Parameter- ganzzahlige Zufallszahl
void anzeigesortiert(int a,int b, int c) // Typ der Funktion void
Dr. Norbert Spangler /Informatik
21.11.2012
20
Funktionsblock
Der Funktionsblock folgt auf den Funktionskopf innerhalb eines Paares
geschweifter Klammern.
Er kann alle bekannten Befehle enthalten. Insbesondere ist es möglich, neu
benötigte Größen zu deklarieren. Diese sind dann lokale Größen in diesem
Block und nach Verlassen des Blocks, d.h. nach Beendigung der Funktion,
nicht mehr existent. Die formalen Parameter im Funktionskopf sind
Platzhalter für die zu verwendenden Daten aus der aufrufenden Funktion. Sie
sind ebenfalls lokale Variable.
Eine Funktion wird mittels return beendet. Hierbei gibt es 2 Varianten.
Dr. Norbert Spangler /Informatik
21.11.2012
21
Beendigung der Funktion/1
Die Funktion ist vom Typ void ( z.B. void main() )
Ein return ohne zusätzliche Angaben ist erforderlich also z.B.
if ( a< 0 )
{
cout<<"unzulaessige Eingabe";
return;
}
Ein return am Ende des Funktionsblocks, d.h. am Ende der Funktion kann
man auch weglassen.
Dr. Norbert Spangler /Informatik
21.11.2012
22
Beendigung der Funktion/2
Die Funktion ist nicht vom Typ void z.B. double wurzel(double a)
Die Funktion muss mit
return wert;
beendet werden. Dabei ist wert ein Ausdruck vom selben Typ wie die
Funktion also etwa im Falle des Typs double beispielsweise
return 0.0; //Konstante vom Typ double
return x; // Variable vom Typ double
return x*x-1; //Ausdruck vom Typ double
Dr. Norbert Spangler /Informatik
21.11.2012
23
6.3 Verwendung einer Funktion
Will man in einer funktion1 (z.B. main) eine andere funktion2
(z.B. wurzel oder abs) verwenden, so muss man sie
im Falle von Standardfunktionen mittels include bereitstellen bzw.
im Falle eigenentwickelter Funktionen dem Compiler zur
Plausibilitätskontrolle alle relevanten Informationen mitteilen wie
- Was ist der Name der Funktion
- Welchen Typ hat das Ergebnis?
- Wieviele Parameter hat die Funktion?
- Welchen Typ haben diese Parameter?
Dies geschieht durch die Angabe des Prototyps.
Dr. Norbert Spangler /Informatik
21.11.2012
24
Prototyp (Deklaration)
Der Prototyp ist ähnlich dem Funktionskopf in der Definition.
Es kann auf die Angabe der Namen der Parameter verzichtet werden. Der Typ ist
jedoch erforderlich.
Der Prototyp muss vor der ersten Verwendung angegeben sein.
Form:
typderfunktion namederfunktion(parametertyp1,…,parametertypn);
Beispiel double wurzel(double)
void main()
{
double radikand;
double wurzel(double);
…
}
Dr. Norbert Spangler /Informatik
21.11.2012
25
Aufruf der Funktion
Beim Aufruf der Funktion müssen die (formalen) Parameter durch die
aktuell zu verwendenden Parameter ersetzt werden.
void main()
{
double radikand;
double wurzel(double);
cout <<"\n radikand eingeben ";
cin>>radikand;
//Plausibilitaetskontrolle
if ( radikand<0 )// unzulaessige Eingabe
cout <<"\n darf nicht < 0 sein ";
else
//Ergebnis
cout <<"\n Wurzel von "<<a<<" = "
<< wurzel(radikand) <<endl;
}
Dr. Norbert Spangler /Informatik
Der aktuelle Parameter für die
Funktion wurzel ist hier
radikand, eine Variable aus
dem main!!!
Hier darf auch ein ausdruck
stehen wie
wurzel (b*b-4*a*c)
Der Ausdruck wird zuerst
berechnet und das Ergebnis
dann als Parameter
verwendet.
21.11.2012
26
Aufruf der Funktion
Ist die Funktion vom Typ void, so erfolgt der Aufruf direkt
Beispiel: Funktion tausche zum Tausch zweier Variabler:
…
tausche(x,y);//die Werte von x und y sollen vertauscht werden
…
Hat die Funktion einen Typ, so muss sie sinvollerweise wie ein R-Wert
behandelt werden:
Beispiele:
loesung1=(-b+wurzel(b*b-4*a*c)/(2*a);
testergebnis=wurzel(x*x);
cout<<" ergebnis "<<wurzel(x+1)<<endl;
Dr. Norbert Spangler /Informatik
21.11.2012
27
Prinzipieller Ablauf des Aufrufs
main
Aufruf, Rücksprungadresse merken
wurzel(b*b-4*a*c);
Aktuelle Parameter auswerten und auf formale Parameter kopieren
wurzel( double a );
Funktionsblock ausführen
(lokale Objekte : a, genauigkeit, x)
Rücksprungadresse holen, Wert mittels return übergeben
main
Dr. Norbert Spangler /Informatik
21.11.2012
28
Parameterübergabe
Beim Aufruf werden die aktuellen Parameter ausgewertet (Ausdrücke)
(lokaler) Speicherplatz wird bereitgestellt für
formale Parameter,
lokale Variable,
Rücksprungadresse
Die Werte der aktuellen Parameter werden auf die formalen Parameter
kopiert
Technik der Parameterübergabe : Call by Value
Die Funktion arbeitet also immer mit Kopien und nie mit Variablen aus
der aufrufenden Funktion. Damit können die aktuellen Parameter aus
der aufrufenden Funktion nicht geändert werden (da nicht bekannt)!
Dr. Norbert Spangler /Informatik
21.11.2012
29
Parameterübergabe
Vorteil
Ausdrücke werden nur ein mal ausgewertet, wenn der Parameter häufig
in der Funktion verwendet wird.
Parameter können nicht versehentlich geändert werden
(Überschreiben von „Konstanten“)
Nachteil
Parameter mit hohem Speicherplatzbedarf werden dupliziert !!!
Dr. Norbert Spangler /Informatik
21.11.2012
30
Beispiel
void main()
{
double wurzel(double);
…
double w1=wurzel(b*b-4*a*c);
double w2=wurzel(b*b+4*a*c);
…
}
Aufruf von wurzel in main:
double wurzel(double a)
{
const double genauigkeit=0.001;
double x=a;
if (a==0.0)
return 0.0;
else
while ( abs (x*x-a)>genauigkeit )
x=x-(x*x-a)/(2*x);
return x;
}
Ausdruck ( z.B. b*b-4*a*c )auswerten
Ergebnis nach a von wurzel kopieren !!!!
->Achtung: a gibt es sowohl im main als auch in wurzel!
Stelle für Rücksprung merken
nach wurzel springen
Ergebnis x berechnen
Nach Rücksprungstelle springen und x übergeben(nach w1 bzw. w2 speichern)
Weiter in main
Dr. Norbert Spangler /Programmieren
21.11.2012
31
6.4 Arrays als Parameter
Call by Value als Übergabetechnik kommt nur bei Variablen zum Einsatz.
Das Anlegen von "Kopien" ist da akzeptabel.
Bei Arrays ist es dagegen weniger sinnvoll. Daher wird in diesem Fall der
Funktion die Anfangsadresse des Arrays in der aufrufenden Funktion
übermittelt. Damit kann die Funktion mit den Originaldaten arbeiten und
benötigt keine Kopien.
Dr. Norbert Spangler /Informatik
21.11.2012
32
Beispiele/Funktionen
void arrayin ( int n, char p_x[]) // Definition
{//Array lesen
for (int i=0;i<n;i++)
cin>>p_x[i];
}
void arrayout ( int n, char p_x[])
{//Array ausgeben
cout<<endl;
for (int i=0;i<n;i++)
cout<<p_x[i];
cout<<endl;
}
void arraycopy ( int n, char p_x[],char p_y[])
{//Array kopieren
for (int i=0;i<n;i++)
p_y[i]=p_x[i];
Mehrere Arrays
}
sind möglich
Dr. Norbert Spangler /Programmieren
Durch Angabe der
Indexklammern [ ] erkennt der
Compiler, dass der
entsprechende Parameter ein
Array ist. Beim Aufruf der
Funktion wird somit die
Anfangsadresse des aktuellen
Parameters übergeben.
Durch den Typ wird die Länge
der Arrayelemente festgelegt.
Die Anzahl er Elemente wird
benötigt.
Die Elemente können damit
geändert werden
- Keine Kopie !!!!!
21.11.2012
33
Beispiel/main
void main()
{
char x[10],y[10];
void arrayin( int,char[]); // Prototypen
void arraycopy( int,char[],char[]);
void arrayout( int,char[]);
cout<<"Anzahl der Elemente ";
int n;
cin>>n;
arrayin(n,x); // Aufrufe
arraycopy(n,x,y);
arrayout(n,y);
}
Durch Verwendung der
Indexklammern [ ]
erkennt der Compiler,
dass der entsprechende
Parameter ein Array ist.
Aktueller Parameter ist jeweils
nur der Name des Arrays ohne
Indexklammern
Dr. Norbert Spangler /Informatik
21.11.2012
34
Eindimensionale Arrays als Parameter
Prototyp: typfunktion funktionsname( …, typarray[] , ….);
-> im Prototyp ist nach dem Typ eine [] anzugeben bei Arrays
In der Regel ist ein zusätzlicher Parameter vom Typ int für die Anzahl der Elemente
notwendig.
Beispiele:
void arrayin( int,char[]);
double summe(int,double[]); //Summation eines Arrays vom Typ double
int summe(int,int[]); //Summation eines Arrays vom Typ int
Aufruf: :
funktionsname( …, namearray , ….);
-> im Aufruf ist nur der Name des Arrays ohne [] anzugeben.
Die Anzahl der verwendeten Komponenten steht in dem zusätzlichen Parameter.
Beispiele s=summe(n,messwerte); // Aufruf mit Rückgabewert (R-Wert!!!)
arrayin(n,x); // Aufruf einer Funktion vom Typ void
Dr. Norbert Spangler /Informatik
21.11.2012
35
Eindimensionale Arrays als Parameter
// Definition
typfunktion namefunktion( …, int anzahl,typarray namearray[],…)
{}
In der Definition ist auf der entsprechenden Parameterposition der
Name des formalen Parameters mit den Indexklammern [] anzugeben.
Zusätzlich hat man in der Regel einen Parameter vom Typ int für die
Anzahl der Elemente.
Beispiel:
void arrayin ( int n, char p_x[])
{//Array lesen
for (int i=0;i<n;i++)
cin>>p_x[i];
}
Dr. Norbert Spangler /Informatik
21.11.2012
36
Mehrdimensionale Arrays als Parameter
Bei mehrdimensionalen Arrays müssten bis auf die erste Dimension alle
anderen angebenen werden. Dies betrifft Prototyp und Definition.
Nur so kann die "Länge" des Elements und damit die Adressierung
sichergestellt werden.
Prototyp
void verarbeitematrix (…, double [][10],…)
void funktion(…,int [][31][24],…)
Definition
void verarbeitematrix (…, double mat [][10],…) {…};
void funktion(…,int datenarray[][31][24],…) {…};
Beim Aufruf dagegen ist wie bisher nur der Name des Arrays
anzugeben:
verarbeitematrix (mat,n,m);
funktion(dat,k,l,m);
Dr. Norbert Spangler /Informatik
21.11.2012
37
Mehrdimensionale Arrays als Parameter/Beispiel
void mataus(int m,int n,int mat[][4])
{
for ( int i=0;i<m;i++ )
{
cout<<endl;
for ( int j=0;j<n;j++ )
cout<<setw(3)<<mat[i][j];
}
cout<<endl;
}
void main()
{
int zahl[5][4]={ {1,2,3},{4,5,6},{7,8,9},{10,11,12}};
void mataus(int,int,int[][4]);
mataus(4,3,zahl);//Ausgabe von 4 Zeilen/3 Spalten
mataus(5,5,zahl);//!!!!!
}
Dr. Norbert Spangler /Informatik
Nicht deklarierte
Elemente werden mit 0
initialisiert.
Der Zugriff auf Elemente
ausserhalb des Arrays
wird nicht abgefangen
21.11.2012
38
6.5 Signatur
Aufgabe
Es soll die Wurzel einer Zahl näherungsweise berechnet werden
Version 1: Genauigkeit 5 Stellen Argument/Ergebnis float
Version 2: Genauigkeit 10 Stellen Argument/Ergebnis double
Prototypen
float wurzel(float);
double wurzel(double);
Es gibt kein Problem, da der Compiler anhand der Argumenttypen beide
Funktionen trotz des identischen Namens unterscheiden kann.
Das Unterscheidungsmerkmal wird als Signatur bezeichnet.
Dr. Norbert Spangler /Informatik
21.11.2012
39
Signatur
Unter der Signatur einer Funktion versteht man ihren Namen, sowie
Anzahl und dem Typ der Parameter unter Berücksichtigung der
Reihenfolge.
Dies ist wichtig, um Funktionen mit gleichem Namen
unterscheiden zu können. Der Compiler vergleicht die
Signaturen und ruft dann entsprechend passende auf.
Es wird ein Link-Fehler angezeigt, wenn es keine passende
Funktion gibt.
Die unterschiedliche Signatur sichert also die einheitliche
Verwendung von Funktionsnamen für identische Aufgabentypen mit
unterschiedlichen Datentypen.
Dr. Norbert Spangler /Informatik
21.11.2012
40
Funktionen für unterschiedliche Typen
Es gibt zahlreiche Aufgabenstellungen, wo nur der Typ der verwendeten
Größen variiert, der Algorithmus jedoch identisch ist.
- Mathematische Formeln in Abhängigkeit vom Datentyp (analog wurzel)
- Vertauschen von Objekten vom identischen Typ
Standardalgorithmus:
typ h=x;
x=y;
y=h;
- Suchen in Arrays
- Sortieren von Arrays
Es gibt eine elegante Möglichkeit in C++, anstelle der "vielen" Funktionen
für jeden möglichen Parametertyp nur eine einzige zu schreiben:
Funktionstemplates.
(siehe letzten Abschnitt in diesem Kapitel).
Dr. Norbert Spangler /Informatik
21.11.2012
41
6.6 Rekursion / Beispiele
Rekursive Definition sind in der Mathematik sehr verbreitet
{
1
n*(n-1)!
falls n=0
falls n>0
{
1
x*x (n-1)
falls n=0
falls n>0
Fakultät
n! =
Potenz
xn =
Fibonaccizahlen
F0=0
F1 = 1
Fn = Fn-1 + Fn-2
Dr. Norbert Spangler /Informatik
21.11.2012
42
Programmierung
Programmierung einer rekursiven Funktion in C++:
In einer Funktion kann dieselbe Funktion wieder aufgerufen werden
int fakultaet(int n)
{
if ( n==0 )
return 1;
else
return n*fakultaet(n-1);
}
double potenz(double x,int n)
{//für n>=0
if ( n==0 )
return 1;
else
return x*potenz(x,n-1);
}
Dr. Norbert Spangler /Informatik
int fibonacci (int n)
{
if ( n==0 )
return 0;
else if ( n==1 )
return 1;
else
return fibonacci(n-1)+fibonacci(n-2);
}
21.11.2012
43
Definition
Rekursion (lat. recurrere) bedeutet Selbstbezüglichkeit
d.h. etwas verweist auf sich selbst.
Interpretation a:
Eine Aufgabe lässt sich zerlegen in eine „einfach“ zu lösendes Teilaufgabe
und eine weitere Aufgabe, welches vom selben Typ ist wie die Ausgangsaufgabe
mit geringere Komplexität bzw. geringeren Umfang, die für „kleine“ Komplexität
direkt lösbar ist.
Beispiele:
einfach
fakultät(n) =
n
*
Sortiere(n Zahlen) = größte suchen +
Dr. Norbert Spangler /Informatik
derselbe Typ/geringerer Umfang
fakultaet(n-1)
sortiere(n-1 Zahlen)
21.11.2012
44
Definition
Rekursion (lat. recurrere) bedeutet Selbstbezüglichkeit
d.h. etwas verweist auf sich selbst.
Interpretation b: Divide and Conquer (Teile und Herrsche)
Eine Aufgabe lässt sich zerlegen in zwei Teilaufgaben welche vom selben Typ
sind wie die Ausgangsaufgabe aber mit geringere Komplexität bzw. geringeren
Umfang. Für „ganz“ einfache Fälle ist die Aufgabe direkt lösbar.
Beispiel Sortieren
Zerlege das Array in 2 Teile
Sortiere ( Teil 1) und Sortiere ( Teil 2)
Mache aus den 2 sortierten Teilen ein sortiertes Array
Einfacher Fall: nur 1 Zahl
Beispiel Suchen
Teste die Mitte – falls gefunden->fertig
Falls gesuchtes Element <: Suche links weiter
Falls gesuchtes Element größer-> Suche rechts weiter
Dr. Norbert Spangler /Informatik
21.11.2012
45
Definition
Was bedeutet geringere Komplexität / geringerer Umfang ?
Zahlen
kleinere Zahlen statt große Zahlen (vgl. Fakultät, Fibonacci)
Daten
wenige Daten statt viele Daten ( vgl. Suchen/Sortieren)
Vektoren/Tabellen wenige Elemente/Zeilen statt vieler Elemente/Zeilen
Ausdrücke
einfache Ausdrücke statt geschachtelte
( i+1 <-> (((i+1)*f(2*x)+.... )
Dr. Norbert Spangler /Informatik
21.11.2012
46
Alternativen
Anstelle der Rekursionen sind oft auch Schleifen möglich:
Iterative Lösung
int fakultaet(int n)
{
int ergebnis=1;
for ( int i=1;i<=n;i++)
ergebnis*=i;
return ergebnis;
}
double potenz (double x,int n)
{
int ergebnis=1;
for ( int i=1;i<=n;i++)
ergebnis*=x;
return ergebnis;
}
Dr. Norbert Spangler /Informatik
int fibonacci(int n)
{
if ( n==0) return 0;
if ( n==1) return 1;
int a=0,b=1,c;
for ( int i=2;i<=n;i++)
{
c=b+a; //Addition
a=b;
b=c;
}
Dies ist meistens möglich.
Es gibt aber Gegenbeispiele
21.11.2012
47
Beispiel Ackermannfunktion
A(0,n)
= n+1
A(m+1,0)
= A(m,1)
A(m+1,n+1)
= A(m,A(m+1,n)
Hierfür gibt es keine Schleifenlösung.
Dr. Norbert Spangler /Informatik
21.11.2012
48
Zusammenfassung
Rekursive Programme
sind in der Regel kürzer/einfacher zu formulieren
Iterationen (Schleifen)
sind in der Regel effizienter
(Overhead durch Funktionsaufrufe fehlt)
Häufig lassen sich Rekursionen durch Iterationen ersetzen.
„Faustformel“ für „übliche“ Aufgaben:
Jede Aufgabe lässt sich rekursiv lösen!
Jede Rekursion lässt sich mittels Schleife realisieren!
Dr. Norbert Spangler /Informatik
21.11.2012
49
Beispiel Suche
Man testet ob es das letzte Element ist und sucht, falls nicht
gefunden, im Rest mit derselben Technik.
Ist kein Element mehr da zum Vergleich, dann wird nichts
gefunden.
int suche (int n, int a[],int element)
{
if ( n==0 )
return -1;//nicht gefunden
else if ( a[n-1] ==element )
return n-1;//gefunden
else
return suche(n-1,a,element);// im Rest weitersuchen
}
Dr. Norbert Spangler /Informatik
21.11.2012
50
Türme von Hanoi
Start
Hilfe
Ziel
n Scheiben
Alle Scheiben müssen von
Start nach Ziel unter
Verwendung von Hilfe
umgelegt werden, wobei
immer nur eine Scheibe
bewegt werden und eine
größere Scheibe nie auf eine
kleinere gelegt werden darf.
Tuerme(int anzahl,char start,char hilfe,char ziel)
{
if ( anzahl>0 )
{
Tuerme(anzahl-1,start,ziel,hilfe);
cout<<"von "<<start<<" nach "<<ziel<<endl;
Tuerme(anzahl-1,hilfe,start,ziel);
}
}
Dr. Norbert Spangler /Informatik
21.11.2012
51
Pythagoräischer Baum
Baum(Seite)
falls Seite zu kurz -> Ende
errichte über der Seite ein Quadrat
zeichne über der gegenüberliegenden Seite ein Dreieck mit vorgegebenen Winkeln
Baum(neue Dreieckseite1)
Baum(neue Dreieckseite2)
Dr. Norbert Spangler /Informatik
21.11.2012
52
6.7 Testen von Funktionen
Main
f_eingabe
f_verarbeitung
f_algorithmus1
f_c
f_a
f_b
f_ausgabe
f_algorithmus2
f_c
f_c
f_d
f_c
Ein System mit mehreren Funktionen soll entwickelt werden.
Problem: Vorgehensweise
Orientiert man sich an der Hierarchie spricht man von inkrementellem Testen
Dr. Norbert Spangler /Informatik
21.11.2012
53
Testen von Funktionen/ Top Down
Main
f_eingabe
f_verarbeitung
f_algorithmus1
f_a
f_b
f_ausgabe
f_algorithmus1
f_c
f_d
Problem: Wenn main programmiert wird und dann getestet werden soll,
stehen die benötigten Funktionen (hier 3 ) noch nicht zur Verfügung.
Lösung: Es sind sogenannte Stubs mit identischen Schnittstellen
(Parametern) zu erstellen, welche das Verhalten der benötigten Funktionen
in einfachster Weise simulieren.
Beispiel:
f_eingabe: Es werden keine Daten gelesen sondern fest vorgegeben
f_ausgabe: Eine einfache Bildschirmzeile: Aufruf Ausgabe wird erstellt
f_verarbeitung: es wird ein plausibles Ergebnis fest vorgegeben.
Dr. Norbert Spangler /Informatik
21.11.2012
54
Testen von Funktionen/ Bottom up
Main
f_eingabe
f_verarbeitung
f_algorithmus1
f_a
f_b
f_ausgabe
f_algorithmus1
f_c
f_d
Problem: Wenn eine Funktion programmiert wird und dann getestet werden
soll, steht die benötigte aufrufende Funktion und insbesondere main noch
nicht zur Verfügung.
Lösung: Es sind sogenannte Treiber zu erstellen, welche die
programmierten Funktionen für Testzwecke aufrufen.
Ein Treiber liest also (nur) Testdaten ein, welche genau diese Funktion
benötigt und zeigt das Ergebnis an.
Beispiele: ab sofort in jedem Praktikum
Dr. Norbert Spangler /Informatik
21.11.2012
55
Top Down
Gegenüberstellung Top Down/Bottom up (Auswahl)
Vorteil : main wird als erstes erstellt;
Vorführung und Einbeziehung der Anwender ist sehr früh möglich.
Frühzeitige Erkennung von Entwurfsfehlern.
Einfachere Tests, da die Funktionen nur in der späteren Einsatzumgebung
getestet werden (müssen) - keine unnötigen theoretischen Tests.
Nachteil: Es müssen Stubs erstellt werden.
Die Funktionen werden nur in der späteren Einsatzumgebung getestet,
sind daher nicht universell getestet
Bottom up
Vorteil : Funktionstest unabhängig von der Einsatzumgebung
Die Funktionen werde sehr ausführlich getestet
Nachteil: Es müssen Treiber erstellt werden.
Vorführung und Einbeziehung der Anwender ist erst spät möglich.
Spätes Erkennen von Entwurfsfehlern.
Dr. Norbert Spangler /Informatik
21.11.2012
56
Testpläne für Funktionen
Als " Eingabedaten" werden hier ausschliesslich Variationen der
Parameter betrachtet. Damit gibt es keine unzulässigen Eingaben wie
char statt int, denn die Parameter haben immer typkonforme Werte.
Beispiel: double wurzel(a)
Fall Beschreibung
1 Siehe Kapitel 1
4
5
Unzulässig
Zeichen
Dr. Norbert Spangler /Informatik
a
Ergebnis
-1.0
?
b kann es nicht geben->keine Eingabe
21.11.2012
57
6.9 Sonstiges / Inline
Der Mechanismus der Parameterübergabe mit dem "hohen" organisatorischen
Mehraufwand
Erstellen der Kopien der Parameter
Abspeichern der Rücksprungadresse
Rückgabe des Ergebnisses
Rücksprung
Entfällt. Stattdessen wird der Programmcode der Funktion direkt am jede
Stelle des Aufrufs eingebaut.
Vorteil: schneller
Nachteil:
längerer Code
Anwendung:
Schlüsselwort inline im Prototyp voranstellen
inline typ name(Parameterliste);
Dr. Norbert Spangler /Informatik
21.11.2012
58
Sonstiges/Funktionstemplate
Ein Funktionstemplate legt die Anweisungen einer Funktion fest, wobei
statt eines konkreten Typs ein Parameter verwendet wird.
Damit kann "eine" Funktion für unterschiedliche Typen programmiert
werden.
Der Typ T ist der Parameter für die Vorlage.
Der Aufruf kann mit oder ohne „Parameter“ erfolgen.
Der Typ wird anhand der aktuellen Parameter bestimmt.
Anmerkung: es können auch mehrere Typen als "Parameter" zum Einsatz
kommen
Dr. Norbert Spangler /Informatik
21.11.2012
59
Funktionstemplate/Beispiel
Angabe der verwendeten Typen
//Summation eines Arrays
#include <iostream>
using namespace std;
template <class T> T summe(int n, T vec[])
{
T help=0;
for ( int i=0; i<n; i++ )
help+=vec[i];
return help;
}
void main()
{
int vec1[]={1,3,5,7,9};
double vec2[]={2.1,2.2,2.3};
Verwendung des Typs
Aufruf
int s1=summe(5,vec1);//Aufruf Variante 1- Typ ist int
double s2=summe<double>(3,vec2);//Aufruf Variante 2
cout << " summe int "<<s1 <<endl;
cout << " summe double "<<s2 <<endl;
}
Dr. Norbert Spangler /Informatik
21.11.2012
60
Herunterladen