Datentypen

Wir zeigen Dir, wie's geht!

Numerische Variablen und Zeichenketten

Im vorherigen Abschnitt hatten wir bereits numerische Variablen (my_var) und Zeichenketten (my_name). Variablen werden automatisch generiert, wenn ihnen ein Wert zugewiesen wird. Im Gegensatz zu vielen anderen dynamisch interpretierten Sprachen können in NumeRe numerische Variablen nicht in Zeichenketten oder umgekehrt umgewandelt werden, d.h., Variablen verbleiben für die aktuelle Sitzung in dem Typ, in dem sie angelegt wurden.

Es ist allerdings möglich, numerische Variablen von rein reellen Werten zu komplexen Werten und wieder zurück umzuwandeln. Dazu ist es nur nötig, eine imaginäre Komponente zu ergänzen bzw. sie durch Nullsetzen zu entfernen.

Was natürlich ebenfalls möglich ist, ist das Konvertieren der Variablenwerte in andere Typen. Das kann entweder mit dem # durchgeführt werden, oder indem man eine der Funktionen to_string(), to_value(), valtostr() verwendet, wobei deren Verhalten sich teilweise stark unterscheidet.

<- to_string(my_var)

-> ans = "my_var"

<- to_value("1+2+3")

-> ans = 6

<- valtostr(my_var)

-> ans = "4.712389"

Tabellen

Die Notation der folgenden, komplexeren, Datentypen orientiert sich an dem Grundsatz: Was unterschiedlich ist, sollte auch unterschiedlich aussehen. Man erkennt besondere Datentypen in NumeRe entsprechend direkt an ihrem Aufruf.

Tabellen werden über den Ausdruck TABELLE(), also mit Klammern, angesprochen. Sie haben also eine ähnliche Syntax wie Funktionen. Um eine Tabelle zu erzeugen, verwendest Du das Kommando new:

<- new my_table()

Tabellen dienen zur Verwaltung assoziativer Datensätze, d.h. multiplen Datenreihen, denen ein oder mehrere Eigenschaften zugeordnet werden können. Das können einfache numerische Werte sein, z.B. der gemessene Strom I bei einer angelegten Spannung U über einen Widerstand R. Es können aber auch komplexere Datenreihen sein, die numerische Werte und Zeichenketten gemischt enthalten können, wie ein Personenregister mit Namen, Alter, Adresse und Körpergröße.

Tabellen bilden Datenreihen in Zeilen ab, so dass die Eigenschaften in Spalten verfügbar sind. Jede Spalte kann dabei einen anderen Datentyp enthalten. Als Beispiel hier einmal eine Datenreihe aus dem genannten Personenregister:

Name Alter Adresse Körpergröße

"Erik" 99 "Hinterm Mond links, 123" 1.88

[...]

Um eine Spalte aus dem Datensatz zu extrahieren, kann my_table(:, 4) verwendet werden. Im obigen Beispiel würde das dann die vierte Spalte, also die Körpergrößen aller Personen zurückgeben. Um eine Datenreihe zu extrahieren, drehst Du einfach die Logik um und schreibst my_table(1, :). Das gibt Dir dann die Einträge der ersten Zeile der Tabelle zurück (Überschriften zählen nicht zu den Zeilen hinzu). Natürlich kannst Du in beiden Fällen auch die andere Dimension einschränken, z.B. my_table(1, 1:3) um nur Name, Alter und Adresse der ersten Datenreihe zu lesen.

<- my_table(:, 4)

-> ans = {1.88, 1.46, 1.85, 1.23, ...}

<- my_table(1, :)

-> ans = {"Erik", 99, "Hinterm Mond links, 123", 1.88}

<- my_table(1, 1:3)

-> ans = {"Erik", 99, "Hinterm Mond links, 123"}

Ebenso wie Du Daten aus der Tabelle gelesen hast, kannst Du auch neue Daten reinschreiben:

<- my_table(1, 2:4) = {88, "Hinterm Mond rechts, 11", 2.12}

-> ans = {88, "Hinterm Mond rechts, 11", 2.12}

Du kannst auch die Spaltenüberschriften lesen und schreiben, wenn Du als Zeilenindex # verwendest:

<- my_table(#, :)

-> ans = {"Name", "Alter", "Adresse", "Körpergröße"}

Eine paar abschließende Informationen:

  • Tabellen haben eine Autosave-Funktion. Sie werden regelmäßig in die numere.cache-Datei gespeichert und sind daher im Gegensatz zu allen anderen Datentypen bei einem Neustart auch automatisch wieder verfügbar.

  • Um Tabellen zu entfernen und den Speicher wieder freizugeben, verwendest Du das Kommando remove my_table(). Willst Du nur den Inhalt der Tabelle löschen, verwendest Du dagegen clear my_table() (oder delete my_table(:, :), wenn Du nur Teile der Tabelle löschen willst). Um dazu noch die Bestätigung zu unterdücken, kannst Du den Parameter -ignore anhängen: clear my_table() -ignore

Cluster

Cluster sind, wie der Name bereits vermuten lässt, unstrukturierte Daten. Sie werden über den Ausdruck CLUSTER{}, also mit geschweiften Klammern, angesprochen. Ein Cluster kann numerische Werte und Zeichenketten gemischt enthalten und eignet sich daher gut, um den Inhalt einer Datenreihe einer Tabelle abzubilden. Tatsächlich sind die gelesenen Daten einer Datenreihe intern bereits ein Cluster. Um ein Cluster zu erzeugen, genügt es, einem Identifizierer mit geschweiften Klammern ein oder mehrere Werte zuzuweisen. Ein explizites Kommando zum Erzeugen wird nicht benötigt:

<- my_cluster{} = {_pi, "Hello, World!", 42}

-> ans = {3.141593, "Hello, World!", 42}

Ein Cluster ist eindimensional, d.h., es kann nur einen Index verwenden, um gelesen oder geschrieben zu werden:

<- my_cluster{2:}

-> ans = {"Hello, World!", 42}

In diesem Beispiel hast Du dir also alle Elemente im Cluster ab Element zwei ausgeben lassen.

Tabellen als Matrizen

Wir begreifen NumeRe in erster Linie als eine Tabellenkalkulation mit einem stark erweiterten Funktionsumfang. Der hauptsächliche Datentyp sind daher Tabellen, die spaltenweise organisiert sind. In erster Linie wirst Du dann Zeilen und Spalten einzeln verarbeiten. Aber natürlich können die Elemente einer Tabelle auch eine Matrix repräsentieren und entsprechend zusammengehörig verarbeitet werden. Das einzige, was Du dazu brauchst, ist das matop Kommando (für "Matrix Operations"). Übergibst Du diesem Kommando einen Ausdruck, so wird dieser mit Matrix-Algebra verarbeitet. So kannst Du mit diesem Kommando auch recht leicht Matrizen erzeugen:

<- matop my_mat() = {{1,2,3}, {4,5,6}, {7,8,8}}

/ 1, 4, 7 \

-> | 2, 5, 8 |

\ 3, 6, 8 /

Die (in diesem Fall automatisch erzeugte) Tabelle my_mat() enthält daraufhin die erzeugte 3x3 Matrix. Du kannst dann zum Beispiel die Determinante und in der Folge auch mittels der Funktion invert() die Inverse der Matrix bestimmen:

<- matop det(my_mat())

-> (3)

<- matop invert(my_mat())

/ -2.666667, 3.333333, -1 \

-> | 2.666667, -4.333333, 2 |

\ -1, 2, -1 /

Du kannst dann auch gerne verifizieren, dass es sich um die Inverse der Matrix handelt, indem Du mit dem Operator ** eine Matrix-Multiplikation ausführst (der normale Multiplikations-Operator * führt hingegen eine komponentenweise Multiplikation aus):

<- matop my_mat() ** invert(my_mat())

/ 1, 1.776357e-015, 0 \

-> | -8.881784e-016, 1, 0 |

\ 0, 0, 1 /

Wie Du siehst, kommt es hier zu Rundungsfehlern. Diese sind aber in der Größenordnung der relativen Genauigkeit des zugrundeliegenden double Datentyps und daher nicht gänzlich zu vermeiden. Mit dem **-Operator kannst Du auch eine Matrix-Vektor-Multiplikation schreiben. Wenn wir z.B. den Vektor {1,2,3} um 22.5° um die z-Achse drehen wollten, könnten wir schreiben:

<- a = radian(22.5)

-> ans = 0.3926991

<- matop {{cos(a),sin(a),0}, {-sin(a),cos(a),0}, {0,0,1}} ** {1,2,3}

/ 0.1585127 \

-> | 2.230442 |

\ 3 /

Daneben gibt es noch eine größere Zahl weiterer Matrixfunktionen, wie z.B. die Berechnung von Eigenwerten mit eigenvals() oder das Eigenvektor-System mit eigenvects().