Einführung in Mathematica

Carsten Rezny

Listen und Funktionen: Map, Apply

Es sollen die Elemente einer Liste aufaddiert werden:

liste={2,4,6,8}

Plus[liste]

Mit Plus allein ist das nicht zu machen. Stattdessen:

Apply[Plus,liste]

Oder auch

Plus@@liste

Allgemein wendet Apply die gegebene Funktion auf alle Elemente einer Liste gemeinsam an.

f@@{x,y,z}

Man kann eine Funktion auf jedes Element einer Liste einzeln anwenden. (Meist geschieht das implizit, wenn eine Listable-Funktion auf eine Liste angewandt wird.) Explizit geht das so:

Map[Sqrt,liste]

In Operatorschreibweise:

Sqrt/@liste

Nun sollen die Elemente mehrerer Listen jeweils addiert werden.

liste=RandomInteger[20,{5,3}]

Plus@@liste

Das war nicht gemeint. Stattdessen müssen Apply und Map kombiniert werden:

Map[Apply[Plus,#]&,liste]

oder

Plus@@#&/@liste

Pure Funktionen

Das vorige Beispiel verwendet eine neue Schreibweise. Das erste Argument von Map oder Apply ist eine sogenannte pure Funktion (pure function), d.h. die Funktion selbst wird als Argument übergeben. Einstellige Funktionen kann man einfach durch ihren Namen angeben. Für mehrstellige Funktionen oder komplexere Ausdrücke gibt es den Funktionsoperator &.

Der Operator & markiert einen vorangehenden Ausdruck als anonyme Funktion. Als Platzhalter für Funktionsargumente dienen #, #1, #2 usw. Die anonyme Funktion kann natürlich gleich mit Argument verwendet werden.

((#)^2)+1&[5]

Auf diese Weise kann man auch benannte Funktionen definieren:

p:=#1^2+#2^2&

p[3,4]

Sinnvoll sind sie aber hauptsächlich im Zusammenhang mit Map, Apply und Co.

Map[(1/(((#)^2)+1))&,Range[10]]

Mehr Listenverarbeitung

Manchmal ist bei Map neben den Listenelementen auch deren Position in der Liste interessant. Dies bietet MapIndexed:

MapIndexed[pp[#1,#2]&,{4,3,5,2}]

Der Index des jeweiligen Elements wird dabei im zweiten Argument als einelementige Liste an die Funktion überreicht. Der Index selbst ist dann also #2[[1]].

Mit Nest wird eine Funktion iteriert. Zu der zu iterierenden Funktion wird ein Startwert und die Anzahl der Iterationen mitgegeben.

Nest[((#)^2)&,2,5]

Wenn die Zwischenergebnisse interessant sind, verwendet man NestList:

NestList[((#)^2)&,2,5]

Wenn statt der Anzahl der Iterationen ein Abbruchkriterium verwendet werden soll, geht das mit NestWhile.

NestWhile[#/2&,1,#>(10^(-4))&]

N[%]

Mit Select können Elemente einer Liste mit beliebigen Kriterien ausgewählt werden.

liste=Sort@RandomInteger[100,30]

Select[liste,PrimeQ]

Prozedurale Programmierung

Neben der funktionalen Programmierung gibt es auch die Möglichkeit zur prozeduralen Programmierung, wie sie aus Sprachen wie C, Java oder Fortran bekannt ist.

Sequenzen von Anweisungen

Mathematica unterscheidet nicht zwischen Anweisungen und anderen Ausdrücken. Jede Anweisung kann als Teil eines Ausdrucks verwendet werden, und jeder Ausdruck kann anstelle einer Anweisung stehen.

2+(a=7)

a

Die Zuweisung a=7 hat als Teil eines Ausdrucks den Wert 7.

Wenn einem Ausdruck ein Semikolon folgt, hat er statt seines normalen Wertes den Wert Null, der nicht ausgegeben wird. Außerdem kann ein Semikolon als Trennzeichen zwischen Ausdrücken stehen. Eine solche mit Semikolons getrennte Folge von Ausdrücken hat den Wert des letzten Ausdrucks (sofern diesem kein Semikolon folgt).

2;3;4

Das ist insbesondere in den folgenden Kontrollstrukturen nützlich.

Bei Bedarf kann eine Sequenz von Ausdrücken auch einfach in runde Klammern gesetzt werden:

(x=1;y=2)+3

Kommentare

Kommentare ermöglichen es, beliebigen Text an beliebigen Stellen in Ausdrücken unterzubringen, z.B. für Dokumentationszwecke. Solche Kommentare werden in Mathematica mit (* und *) umschlossen. Sie werden wie Zwischenraum (Leerzeichen) behandelt.

gg[x_ (* ein Argument *)]:=Table[i ,{i,x (* i läuft von 1 bis x *)}]

Blöcke und Module

Prozedurale Programmierung benötigt häufig lokale Variablen. Dafür stehen zwei Möglichkeiten zur Verfügung. Module erzeugt lexikalische lokale Variablen, d.h., die Variablen existieren nur im Kontext des jeweiligen Module. (Das entspricht z.B. dem Verhalten von my in Perl.) Von dort aus aufgerufene Funktionen sehen diese lexikalischen Variablen nicht.

a=0; b=1;

Module[{a=5},a+b]

Intern werden die als lokal deklarierten Bezeichner durch temporäre Bezeichner ersetzt, was bei undefinierten lokalen Variablen sichtbar wird.

Module[{a},a+b]

f[x_]:=a x
Module[{a=5},f[8]]

Im Unterschied dazu erzeugt Block dynamische lokale Variablen, die auch für aufgerufene Funktionen sichtbar bleiben (analog zu local in Perl).

Block[{a=5},a+b]

Block[{a},a+b]

Block[{a=5},f[8]]

Kontrollstrukturen

Ein weiteres wesentliches Element von prozeduraler Programmierung sind Kontrollstrukturen für Schleifen und bedingte Ausdrücke.

Die einfachste Form einer Schleife ist Do:

Do[k^2,{k,1,10}]

Do produziert von sich aus kein Resultat, funktioniert ansonsten aber genau wie Table.

Do[Print[k," ",k^2],{k,1,10}]

Die For-Schleife ist analog zu dem in C oder Java vorhandenen for aufgebaut:

For[k=1,k<=10,k++,Print[k," ",k^2]]

While wiederholt einen Ausdruck, solange die gegebene Bedingung wahr ist.

x=(10^(-5));While[x<1,x*=2]; x

If verhält sich in etwa wie der aus C oder Java bekannte ternäre Operator ?:

test[x_]:=If[PrimeQ[x],"Primzahl","keine Primzahl"]

test[8]

test[7]

If kennt noch ein viertes Argument, das ausgewertet wird, wenn die Bedingung weder wahr noch falsch ist:

kleiner[x_,y_]:=If[x<y,"kleiner","nicht kleiner","undefiniert"]

kleiner[2,3]

kleiner[5,z]

Für eine Entscheidung unter mehreren Fällen gibt es Switch und Which. Ersteres ähnelt wieder stark seinem C-Pendant:

wort[x_]:=Switch[x,1,"eins",2,"zwei",3,"drei",_,"weiß nicht"]

wort[3]

wort[4]

Which verarbeitet eine Reihe von unabhängigen Test-Wert-Paaren.

x=0;y=1;z=1;Which[x==1,"x",y==1,"y",z==1,"z"]

Clear[x,y,z]

Beispiel

Als Beispiel für prozedurale Programmierung hier eine Funktion, die zwei Elemente einer Liste miteinander vertauscht.

swap[v_List,i_Integer,j_Integer]:=Block[
{tmp=v[[j]] (* Wert zwischenspeichern *),
vv=v (* für das Resultat, da Funktionsparameter nicht veränderbar sind *)},
vv[[j]]=v[[i]];
vv[[i]]=tmp;
vv (* letzter Ausdruck ist Resultat des Blocks *)
]

swap[{1,2,3,4,5},2,4]

Die Parameter einer Funktion sind, wie bereits erwähnt, keine Variablen. Wenn der Wert eines Paramerters verändert werden soll, muss das immer über eine lokale Variable geschehen.