This is documentation for Mathematica 4, which was
based on an earlier version of the Wolfram Language.
View current documentation (Version 11.2)

 Documentation /  Mathematica /  Das Mathematica Buch /  Die Prinzipien von Mathematica /  Evaluierung von Ausdrücken /

Bedingte AnweisungenTrace der Evaluierung

2.5.9 Schleifen und Kontrollstrukturen

Die Ausführung eines Mathematica-Programmes beinhaltet die Evaluierung einer Folge von Mathematica-Ausdrücken. In einfachen Programmen können die zu evaluierenden Ausdrücke durch Semikola getrennt sein und werden einer nach dem anderen evaluiert. Häufig müssen Sie jedoch Ausdrücke mehrmals evaluieren, in einer Art „Schleife".

Einfache Schleifenkonstrukte

Dies evaluiert Print[i^2], wobei i von 1 bis 4 läuft.

In[1]:= Do[Print[i^2], {i, 4}]

Dies führt eine Zuweisung für t in einer Schleife aus, wobei k von 2 bis 6 in Schrittweiten von 2 läuft.

In[2]:= t = x; Do[t = 1/(1 + k t), {k, 2, 6, 2}]; t

Out[2]=

Iteration wird in Do genauso wie in Funktionen wie Table und Sum spezifiziert. Genauso wie in diesen Funktionen können Sie mehrere verschachtelte Schleifen konstruieren, indem Sie Do eine Folge von Iterations-Spezifikationen übergeben.

Diese Schleife läuft über die i-Werte 1 bis 4, und für jeden Wert von i läuft die j-Schleife von 1 bis i-1.

In[3]:= Do[Print[{i,j}], {i, 4}, {j, i-1}]

Mitunter wollen Sie eine bestimmte Operation wiederholen, ohne den Wert einer Iterationsvariablen zu ändern. Sie können diese Art der Wiederholung in Do genauso wie in Table und anderen Iterationsfunktionen spezifizieren.

Dies wiederholt die Zuweisung t = 1/(1+t) dreimal.

In[4]:= t = x; Do[t = 1/(1+t), {3}]; t

Out[4]=

Innerhalb von Do können Sie eine Prozedur einsetzen.

In[5]:= t = 67; Do[Print[t]; t = Floor[t/2], {3}]

Funktionen wiederholt anwenden

Mit Do können Sie Operationen wiederholen und dabei einen bestimmten Ausdruck für verschiedene Werte von Iterationsvariablen viele Male evaluieren. Häufig erhalten Sie jedoch elegantere und effizientere Programme, wenn Sie die Konstrukte der funktionalen Programmierung einsetzen (siehe Abschnitt 2.2.2). Nest[f, x, n] zum Beispiel erlaubt Ihnen, eine Funktion wiederholt auf einen Ausdruck anzuwenden.

Dies verschachtelt f dreimal.

In[6]:= Nest[f, x, 3]

Out[6]=

Durch Verschachtelung einer reinen Funktion erhalten Sie dasselbe Ergebnis wie im Beispiel oben mit Do.

In[7]:= Nest[ Function[t, 1/(1+t)], x, 3 ]

Out[7]=

Mit Nest können Sie eine Funktion eine bestimmte Anzahl von Malen anwenden. Mitunter wollen Sie jedoch eine Funktion so lange anwenden, bis die Ergebnisse, die Sie erhalten, sich nicht mehr ändern. Dies erreichen Sie mit FixedPoint[f, x].

FixedPoint wendet eine Funktion so lange an, bis sich das Ergebnis nicht mehr ändert.

In[8]:= FixedPoint[Function[t, Print[t]; Floor[t/2]], 67]

Out[8]=

Mit FixedPoint können Sie das Evaluierungsverfahren in Mathematica oder die Operation von Funktionen wie ausdr //. regeln nachahmen. FixedPoint fährt fort, bis zwei aufeinanderfolgende Ergebnisse dieselben sind. NestWhile erlaubt Ihnen fortzufahren, bis eine beliebige Funktion nicht mehr True ergibt.

Nichtlokale Kontrolle der Evaluierung

Wenn Throw angetroffen wird, stoppt die Evaluierung und der aktuelle Wert von i wird als der Wert des einschließenden Catch zurückgegeben.

In[9]:= Catch[Do[Print[i]; If[i > 3, Throw[i]], {i, 10}]]

Out[9]=

Throw und Catch bieten eine flexible Methode zur Kontrolle des Evaluierungsprozesses in Mathematica. Die Grundidee ist folgende: Wann immer ein Throw angetroffen wird, wird die Evaluierung, die gerade abläuft, gestoppt, und Mathematica kehrt sofort zum nächsten einschließenden Catch zurück.

Scan wendet die Funktion Print auf jedes aufeinanderfolgende Element in der Liste an. Am Ende wird nur Null zurückgegeben.

In[10]:= Scan[Print, {7, 6, 5, 4}]

Die Evaluierung von Scan stoppt, sobald Throw angetroffen wird, und das einschließende Catch gibt als seinen Wert das Argument von Throw zurück.

In[11]:= Catch[Scan[(Print[#];
If[# < 6, Throw[#]])&, {7, 6, 5, 4}]]

Out[11]=

Dasselbe Ergebnis wird mit Map erreicht, obwohl Map eine Liste zurückgegeben hätte, wenn seine Evaluierung nicht durch das Antreffen eines Throw gestoppt worden wäre.

In[12]:= Catch[Map[(Print[#];
If[# < 6, Throw[#]])&, {7, 6, 5, 4}]]

Out[12]=

Mit Throw und Catch läßt sich die Operation funktionaler Programmierkonstrukte umleiten, um so zum Beispiel die Evaluierung derartiger Konstrukte nur so lange fortzusetzen, bis eine bestimmte Bedingung erfüllt ist. Beachten Sie: Wenn Sie die Evaluierung mit Throw stoppen, kann sich die bis dahin erhaltene Struktur stark von der unterscheiden, die Sie erhalten hätten, wenn Sie die Evaluierung bis zum Ende hätten laufen lassen.

Hier ist eine Liste, die durch wiederholte Anwendung einer Funktion erzeugt wurde.

In[13]:= NestList[1/(# + 1)&, -2.5, 6]

Out[13]=

Da kein Throw angetroffen wurde, ist das Ergebnis so wie zuvor.

In[14]:= Catch[ NestList[1/(# + 1)&, -2.5, 6] ]

Out[14]=

Nun wird die Evaluierung von NestList umgelenkt, und die eine Zahl zurückgegeben, die als Argument von Throw angegeben ist.

In[15]:= Catch[ NestList
[If[# > 1, Throw[#], 1/(# + 1)]&, -2.5, 6] ]

Out[15]=

Throw und Catch operieren auf vollständig globale Weise: es hängt nicht davon ab, wie oder wo ein Throw erzeugt wird—Throw wird immer die Evaluierung stoppen und zum einschließenden Catch zurückkehren.

Das Throw stoppt die Evaluierung von f und bewirkt, daß das Catch nur a zurückgibt, ohne eine Spur von f zurückzulassen.

In[16]:= Catch[ f[ Throw[ a ] ] ]

Out[16]=

Dies definiert eine Funktion, die ein Throw erzeugt, wenn ihr Argument größer als 10 ist.

In[17]:= g[x_] := If[x > 10, Throw[overflow], x!]

Hier wird kein Throw erzeugt.

In[18]:= Catch[ g[4] ]

Out[18]=

Aufgrund des in der Evaluierung von g erzeugten Throw erfolgt hier aber eine Rückkehr zum einschließenden Catch.

In[19]:= Catch[ g[40] ]

Out[19]=

In kleinen Programmen ist es häufig angebracht, Throw[wert] und Catch[ausdr] in ihren einfachsten Formen zu verwenden. Aber besonders dann, wenn größere Programme geschrieben werden, die viele getrennte Teile enthalten, sind Throw[wert, marke] und Catch[ausdr, form] in der Regel vorteilhafter. Dadurch, daß man die Ausdrücke marke und form lokal bezüglich eines Programmteils hält, läßt sich erreichen, daß Throw und Catch nur innerhalb dieses Teils operieren werden.

Hier wird Throw durch das innere Catch aufgefangen.

In[20]:= Catch[ f [ Catch[ Throw[x, a], a ] ], b ]

Out[20]=

Hier wird es aber nur durch das äußere Catch aufgefangen.

In[21]:= Catch[ f [ Catch[ Throw[x, b], a ] ], b ]

Out[21]=

Mit einem Muster läßt sich spezifizieren, welche Marken ein Catch jeweils fangen soll.

In[22]:= Catch[ Throw[x, a], a | b ]

Out[22]=

Dies hält die Marke a vollständig lokal.

In[23]:= Module[{a}, Catch[ Throw[x, a], a] ]

Out[23]=

Man sollte sich bewußt machen, daß die Marke, die in Throw erscheint, nicht notwendig eine Konstante sein muß; es kann ganz allgemein irgendein Ausdruck sein.

In diesem Fall fängt das innere Catch alle Würfe mit Marken kleiner als 4 auf und setzt das Do fort. Sobald aber die Marke 4 erreicht wird, wird das äußere Catch benötigt.

In[24]:= Catch[ Do[ Catch[ Throw[i^2, i], n_ /; n < 4],
{i, 10} ], _]

Out[24]=

Wenn Sie Catch[ausdr, form] zusammen mit Throw[wert, marke] einsetzen, ist der von Catch zurückgegebene Wert einfach der in Throw angegebene Ausdruck wert. Wenn Sie aber Catch[ausdr, form, f] einsetzen, dann ist der von Catch zurückgegebene Wert stattdessen f[wert, marke].

In diesem Fall wird f auf den Wert und die Marke in Throw angewendet.

In[25]:= Catch[ Throw[ x, a ], a, f ]

Out[25]=

Gibt es kein Throw, wird f niemals verwendet.

In[26]:= Catch[ x, a, f ]

Out[26]=

Allgemeine Schleifenkonstrukte

Funktionen wie Do, Nest und FixedPoint bieten strukturierte Methoden zur Konstruktion von Schleifen in Mathematica-Programmen, während Throw und Catch Möglichkeiten bieten, diese Struktur zu ändern. Mitunter müssen Sie jedoch Schleifen erstellen, die von vornherein weniger Struktur haben. In derartigen Fällen kann es vorteilhafter sein, Funktionen wie While und For einzusetzen, die Operationen wiederholt ausführen und stoppen, sobald eine spezifizierte Bedingung nicht wahr ist.

Die While-Schleife fährt fort, bis die Bedingung nicht mehr zutrifft.

In[27]:= n = 17; While[(n = Floor[n/2]) != 0, Print[n]]

Die Funktionen While und For in Mathematica ähneln den Kontrollstrukturen while und for in Sprachen wie zum Beispiel C. Beachten Sie jedoch eine Reihe wichtiger Unterschiede. Zum Beispiel sind die Rollen von Komma und Semikolon in Mathematica-For-Schleifen relativ zu denen in der C-Sprache vertauscht.

Dies ist eine häufig anzutreffende Form einer For-Schleife. i++ inkrementiert den Wert von i.

In[28]:= For[i=1, i < 4, i++, Print[i]]

Hier ist eine kompliziertere For-Schleife. Beachten Sie, daß die Schleife beendet wird, sobald der Test i^2 < 10 False ergibt.

In[29]:= For[i=1; t=x, i^2 < 10, i++, t = t^2 + i;
Print[t]]

In Mathematica evaluieren sowohl While als auch For immer den Schleifentest, ehe sie den Rumpf der Schleife evaluieren. Sobald der Schleifentest nicht mehr True ergibt, terminieren While und For. Der Rumpf der Schleife wird deshalb nur dann evaluiert, wenn der Schleifentest True ergibt.

Der Schleifentest ergibt sofort False, deshalb wird der Rumpf der Schleife niemals evaluiert.

In[30]:= While[False, Print[x]]

In einer While- oder For-Schleife, oder allgemein in jeder Mathematica-Prozedur, werden die von Ihnen eingegebenen Mathematica-Ausdrücke in einer festgelegten Folge evaluiert. Sie können sich diese Folge als Definition des „Kontrollflusses" bei der Ausführung eines Mathematica-Programmes vorstellen.

In den meisten Fällen sollten Sie versuchen, den Kontrollfluß in Ihren Mathematica-Programmen so einfach wie möglich zu halten. Je mehr der Kontrollfluß zum Beispiel von speziellen Werten abhängt, die während der Ausführung des Programmes erzeugt wurden, desto schwieriger wird es in der Regel sein, die Struktur und die Operation des Programms zu verstehen.

Funktionale Programmier-Konstrukte enthalten üblicherweise sehr einfache Kontrollflüsse. While- und For-Schleifen sind immer komplizierter, da sie so konstruiert sind, daß der Kontrollfluß von den Werten der als Test angegebenen Ausdrücke abhängt. Sogar in derartigen Schleifen hängt trotzdem der Kontrollfluß gewöhnlich nicht von den im Schleifenrumpf angegebenen Ausdruckswerten ab.

In einigen Fällen müssen Sie jedoch eventuell Mathematica-Programme konstruieren, in denen der Kontrollfluß von Werten beeinflußt wird, die während der Ausführung einer Prozedur oder des Rumpfes einer Schleife erzeugt wurden. Dies läßt sich, ganz im Sinne der funktionalen Programmierung, mit Throw und Catch erreichen. Mathematica unterstützt aber auch verschiedene Funktionen zur Modifizierung des Kontrollflusses, die in etwa so funktionieren wie in C.

Kontrollflußfunktionen

Break[ ] bewirkt, daß die Schleife terminiert, sobald t größer als 19 wird.

In[31]:= t = 1; Do[t *= k; Print[t];
If[t > 19, Break[]], {k, 10}]

Wenn k < 3 ist, dann bewirkt Continue[ ], daß die Schleife fortgesetzt wird, ohne t += 2 auszuführen.

In[32]:= t = 1; Do[t *= k; Print[t];
If[k < 3, Continue[]]; t += 2, {k, 10}]

Mit Return[ausdr] kann eine bestimmte Funktion verlassen und dabei ein Wert zurückgegeben werden. Throw kann als eine Art nicht-lokaler Rücksprung angesehen werden, mit dem eine ganze Folge verschachtelter Funktionen verlassen werden kann. Ein derartiges Verhalten kann bei der Behandlung bestimmter Fehlerbedingungen erwünscht sein.

Hier ist ein Beispiel für die Verwendung von Return. Diese spezielle Prozedur hätte genausogut ohne Return geschrieben werden können.

In[33]:= f[x_] :=
(If[x > 5, Return[groß]]; t = x^3; Return[t - 7])

Ist das Argument größer als 5, so wird das erste Return in der Prozedur benutzt.

In[34]:= f[10]

Out[34]=

Diese Funktion „wirft" fehler aus, wenn ihr Argument negativ ist.

In[35]:= h[x_] := If[x < 0, Throw[fehler], Sqrt[x]]

Hier wird kein Throw erzeugt.

In[36]:= Catch[ h[6] + 2 ]

Out[36]=

Aber in diesem Fall wird Throw erzeugt, und Catch gibt den Wert fehler zurück.

In[37]:= Catch[ h[-6] + 2 ]

Out[37]=

Mit Funktionen wie Continue[ ] und Break[ ] läßt sich die „Kontrolle" an den Anfang oder an das Ende einer Schleife in einem Mathematica-Programm „übertragen". Mitunter müssen Sie stattdessen die Kontrolle an ein bestimmtes Element in einer Mathematica-Prozedur übertragen. Ist ein Label als Element in einer Prozedur angegeben, so kann die Kontrolle mit Goto an dieses Element übertragen werden.

Die Schleife wird wiederholt ausgeführt, bis q größer als 6 ist.

In[38]:= (q = 2; Label[Anfang]; Print[q]; q += 3;
If[q < 6, Goto[Anfang]])

Beachten Sie, daß Goto in einer bestimmten Mathematica-Prozedur nur dann eingesetzt werden kann, wenn das angesprochene Label als Element derselben Mathematica-Prozedur auftritt. Im allgemeinen reduziert die Verwendung von Goto den Strukturgrad, der ohne weiteres in einem Programm erkannt werden kann, und erschwert deshalb das Verstehen der Operation des Programmes.

Bedingte AnweisungenTrace der Evaluierung