Prinzipien der Softwareentwicklung Programmieren ist eine Kunst, aber es gibt eine Reihe von Regeln, die dabei beachtet werden müssen: Prinzip der Striktheit Prinzip der Strukturierung Prinzip der Abstraktion Prinzip der Lokalität und Geheimhaltung Prinzip der Annahme der Änderungsnotwendigkeit . – p.1/19 Striktheit Striktheit ist u.a. bei folgenden Aufgaben ein Muß: Zielsetzung (was soll das Programm leisten) Dokumentation von Beginn des Projekts an Entwurf (Überlegungen zum Algorithmus) Umsetzung in Programmcode . – p.2/19 Strukturierung Beim Programmentwurf ist zu beachten: Das Programm muß robust gegenüber falscher Eingaben und Laufzeitfehlern sein. Die Ergebnisse müssen nachweisbar korrekt sein. Der Code muß übersichtlich und erweiterbar sein. Der Ressourcenverbrauch (Speicheraufwand) muß minimiert und die Performance optimiert werden. Dabei treten häufig Zwiespälte auf: Performance ↔ Erweiterbarkeit Resourcenverbrauch ↔ Performance . – p.3/19 Abstraktion Trennung von wichtigen und unwichtigen Merkmalen zum Zweck der “Wegabstraktion” der letzteren ermöglicht Vereinfachungen des Algorithmus Reduzierung der Entwicklungszeit Erweiterung des Anwendungsbereiches Erhöhung der Leistungsfähigkeit Beispiel: Es soll ein Modell eines Kanals erstellt werden wichtig unwichtig - Fließgeschwindigkeit - Insekten/Blätter auf der - Form der Kanalbegrenzung Wasseroberfläche - Hindernisse (Brücken) - Wassertemperatur . – p.4/19 Lokalität und Geheimhaltung Lokalität Funktionen greifen – soweit möglich – nur auf die mit ihnen verbundenen Daten zu, so daß keine unkontrollierbaren Seiteneffekte auftreten. Geheimhaltung Funktionen zeigen ihre internen Daten nicht nach außen. Dann können lokale Variablen auch nicht von außen unabsichtlich geändert werden. Interner Aufbau von Funktionen ist nach außen nicht erkennbar. Wird der interne Aufbau einer Funktion nachträglich verändert / verbessert, beeinträchtigt dies das restliche Programm nicht. . – p.5/19 Änderungsnotwendigkeit In der Regel unterliegt ein Softwareprodukt einer stetigen Veränderung mit dem Ziel ... erkannte Fehler zu korrigieren. ... bestehende Software-Routinen in Bezug auf ihr Laufzeitverhalten zu optimieren. ... neue Module zu schreiben, um das Programm mit zusätzlichen Möglichkeiten zu erweitern. Denn: Softwareentwicklung ist kein typischer Fertigungsprozeß, sonder verläuft zyklisch! Dieser Zyklus ist meistens nicht endlich ;-) . – p.6/19 Strukturiertes Programmieren Tipps zur Entwurfsphase: Der Übersichtlichkeit halber sollte der erste Entwurf als Flußdiagramm oder als Pseudocode geschrieben werden. Der Entwurf soll (evtl. von Kollegen) kritisch bewertet und im weiteren Verlauf ggf. nachgebessert werden. Es muß überlegt werden, welche Datenstrukturen für die konkrete Problemstellung am besten geeignet sind. Das Softwareprodukt sollte modular aus Unterroutinen aufgebaut werden, die kleine Teilprobleme lösen. Literaturrecherchen und Internetsuche nach passenden Algorithmen/Software-Komponenten zahlen sich aus. . – p.7/19 Strukturiertes Programmieren Tipps zur Entwicklungsphase: Der Code sollte strikt dem Entwurf entsprechen. Falls dieser nicht zu realisieren ist, muß unbedingt der Entwurf überarbeitet werden. Das Programm sollte möglichst lesbar und verständlich sein (auch nach mehreren Jahren sollte man noch in der Lage sein zu verstehen, was der Code macht.) Programmierarbeiten sind schrittweise durchzuführen, so daß jeder Teil einzeln getestet werden kann. Es sollte auf bestehende (bewährte) Algorithmen und Softwarebibliotheken zurückgegriffen werden. Defensives Programmieren erspart oft die Fehlersuche. . – p.8/19 Programmieren mit MATLAB Modularität und Performance: schrittweiser Aufbau aus Unterroutinen für Teilaufgaben mit Verwendung von optimalen Datenstrukturen und Speicherzugriffen Vorbelegung/Initialisierung von großen Matrizen Vektorisierung und matrixorientierter Programmierung laufzeitoptimierten MATLAB-Funktionen und -Toolboxen effizienter Programmsteuerung (Schleifen nur dort einsetzen, wo sie zwingend notwendig sind) Ressourcenverbrauch: Minimierung des erforderlichen Speicheraufwandes mit allen Mitteln! . – p.9/19 Programmieren mit MATLAB Sicherheit: Vermeidung von Programmabstürzen und falschen Ergebnissen durch ... Überprüfung von Ein- und Ausgabeparametern Abfangen von Fehlern, die während der Rechnung auftreten können, mit Hilfe von try/catch eindeutige Namensgebung und – sofern möglich – keine Verwendung von globalen Variablen Sonstige Anforderungen: Benutzerfreundlichkeit (Hilfe, Dokumentation, GUI) Lesbarkeit (Kommentare, Leerzeichen, Einrückung) Orientierung beim Programmieren an den gängigen Richtlinien zum Layout von M-Files . – p.10/19 Struktur von M-Files Ein M-File soll folgende Informationen enthalten: Name und Typ (Skript oder Funktion) H1-Zeile mit einer Kurzbeschreibung Informationen zur Syntax, ggf. Beispiele Beschreibung von Ein- und Ausgabeparametern Default-Einstellungen für evtl. fehlende Daten Gültigkeitsbereich, bekannte Einschränkungen Liste von Unterroutinen, die aufgerufen werden Autor(en), Version, Datum der letzten Änderung Ausführliche Kommentare im Quelltext!!! . – p.11/19 Kommentare im Quelltext Kommentare müssen während des Programmierens geschrieben werden, nicht danach Quelltext und Kommentare müssen übereinstimmen. Darauf ist besonders bei Modifikationen zu achten Kommentare sollten kurz, verständlich und lesbar sein (mit Leerzeichen, Großbuchstaben, Einrückung) Ausdrücke wie A+B benötigen keine nähere Erklärung! Jeder Kommentar im Quelltext muß zählen Ein schlecht konzipierter oder fehlerhafter Code kann nicht allein durch Kommentare verbessert werden ;-) Ein übersichtlicher Code mit Kommentaren kann von vielen Leuten benutzt und ggf. ausgebaut werden . – p.12/19 Namensgebung und Performance Variablennamen sollten aussagekräftig sein, d.h. in der Regel aus mehr als einem Buchstaben bestehen. Variablen können durch einheitliche Namensgebung sowie Groß- und Kleinschreibung strukturiert werden. Namen aus dem MATLAB-Vokabular sollten nicht für eigene Variablen und Funktionen verwendet werden Funktionen werden schneller ausgeführt als Skripte Zeiten werden mit tic/toc bzw. cputime gemessen Mit Hilfe des Profilers findet man rechenzeitintensive Programmteile, die optimiert werden müssen Nicht bzw. schlecht vektorisierbare Algorithmen können als MEX-Files in Fortran oder C implementiert werden . – p.13/19 Speicherverwaltung in MATLAB Dynamische Vergrößerung von Matrizen ist untersagt; diese sollten mit zeros oder ones vorbelegt werden Dünnbesetzte Matrizen sollten als solche behandelt werden, um den Speicheraufwand zu reduzieren Aufgrund der komprimierten Darstellung von sparse Matrizen ist der Zugriff auf ihre Elemente langsamer MATLAB speichert die Elemente eines Spaltenvektors hintereinander ab, so daß ein sehr schneller Zugriff möglich ist. Zeilenvektoren werden nicht hintereinander abgespeichert, so daß für jeden Eintrag eine kostspielige Indexberechnung notwendig ist. Variablen, die nicht mehr gebraucht werden, sind mit dem Befehl clear aus dem Workspace zu löschen . – p.14/19 Flexibilität und Sicherheit Funktionen lassen optionale Parameter und variable Parameterlisten varargin bzw. varargout zu Die Parameterzahl für den aktuellen Funktionsaufruf läßt sich mit nargin bzw. nargout feststellen Funktionen können so geschrieben werden, daß sie sowohl Zeilen- als auch Spaltenvektoren als Parameter akzeptieren: x=x(:) bzw. x=x(:).’ Kommandos wie eval und feval bieten eine große Flexibilität erschweren aber die Fehlersuche Voraussehbare Laufzeitfehler lassen sich durch evtl. geschachtelte try/catch Blöcke abfangen Der MATLAB-Debugger ermöglicht eine schrittweise Programmanalyse und erleichtert die Fehlersuche . – p.15/19 Fehlersuche: logische Fehler Der Interpreter von MATLAB ist nur in der Lage, syntaktische Fehler zu finden. Inhaltliche Fehler muß der Programmierer selbst erkennen und beseitigen. Bei einer if-Anweisung kann die falsche Verzweigung gewählt werden if x < 0 y = sqrt(x); else warning(’x darf nicht negativ sein.’); end Bei der Betrachtung von mehreren Bedingungen muß auf die logische Hierarchie geachtet werden (y =0 | z =0) & x>=0 (help precedence) . – p.16/19 Fehlersuche: Schleifen Falsche Initialisierung der Iterationsvariablen bzw. des logischen Ausdruckes bei while-Schleifen Fehlende oder nicht erfüllbare Abbruchbedingung (keine Erhöhung des Zählers) bei while-Schleifen while n<100 ... n=n+1 % Darf nicht fehlen end Falsche Anzahl der Schleifendurchläufe, Schrittweite oder Zählrichtung (aufwärts, abwärts) bei for-Schleifen Falsche Anwendung von Kommandos break und continue, insbesondere bei geschachtelten Schleifen . – p.17/19 Fehlersuche: Matrixoperationen Benutzung von mehrdimensionalen Feldern ohne Überprüfung ihrer tatsächlichen Dimension, z.B. ein Funktionsaufruf mit einem Skalar anstelle einer Matrix, die als Eingabeparameter erwartet wird. Zugriff auf Feldeinträge, die außerhalb der zulässigen Dimensionen liegen. v=[1;2;3]; c=v(4); ??? Index exceeds matrix dimensions. Verwechslung von Zeilen- und Spaltenvektoren Falsche logische Indizierung für Teilmatrizen Fehlerhafte Ein- und Ausgabe von Daten . – p.18/19 Testaufgabe “Ringelwurm” Die Dermatophytose (Ringelwurmkrankheit) ist eine durch Pilzinfektion hervorgerufene Hautkrankheit. Der Verlauf einer Infektion läßt sich folgendermaßen modellieren: Ein Hautareal wird durch eine n × m-Matrix symbolisiert, jeder Matrixeintrag ist eine Hautzelle. In einem Zeitschritt kann jede infizierte Hautzelle eine der acht Nachbarzellen mit Wahrscheinlichkeit p ∈ ] 0, 1 [ infizieren. Nach i Zeitschritten wird eine infizierte Zelle wieder gesund; sie ist dann immun für j weitere Zeitschritte. Aufgabenstellung: Schreiben Sie eine MATLAB-Funktion, die auf den Parametern n, m, p, i, j eine Infektion simuliert, die mit einer infizierten Zelle in der Mitte der Matrix beginnt. Idee und Implementierung: Prof. Dr. Jörg Fliege . – p.19/19