Aus Das deutschsprachige Scratch-Wiki

Listen als Log-Dateien verwenden

Eines der Hauptprobleme beim Debuggen von Skripten ist die in Scratch 2.0 fehlende Möglichkeit, den Programmcode in Einzelschritten, also Zeile für Zeile durchlaufen zu lassen. In Scratch 1.4 war die Einzelschritt-Ausführung noch möglich. Andere Programmierumgebungen ermöglichen es, Haltepunkte für einzelne Programmzeilen zu definieren und den weiteren Code ab dem Erreichen dieser Stelle Schritt für Schritt ausführen zu lassen. Der Nutzen dieser Vorgehensweise besteht darin, dass man die Änderung der entscheidenden Variableninhalte und den sich daraus ergebenden Ablaufweg des Programms unmittelbar und im eigenen Arbeitstempo nachvollziehen kann.

Bietet eine Programmierumgebung diese Möglichkeit zur Einzelschrittausführung nicht, behelfen sich Programmierer meist mit dem Erzeugen sogenannter "Log-Dateien". Um nachvollziehen zu können, ob der Programmcode richtig arbeitet, werden dazu einfach an verschiedenen Stellen Informationen zum aktuellen Programmstatus in eine Datei geschrieben. Das können Angaben zu momentanen Variableninhalten sein, oder auch kurze Texte, die anzeigen, dass das Programm jetzt in eine bestimmte Unterroutine gesprungen ist oder diese wieder verlassen hat. Oftmals wird das ganze auch noch mit einem Zeitstempel, also der aktuellen Uhrzeit versehen. Tritt dann während des Testens ein Fehler auf, bricht der Programmierer den Programmablauf ab und kann in der Log-Datei anhand der letzten Eintragungen nachvollziehen, welche Umstände zu dem Fehler geführt haben.

Anzeige von Variableninhalten während des Programmablaufs reicht manchmal nicht aus

Auch in Scratch-Skripten kann man aktuelle Variableninhalte während des laufenden Programms anzeigen lassen. Dies ist z.B. durch die Anzeige der Variable auf der Bühne möglich. Handelt es sich um eine zu einem Klon gehörende Variable, kann man diese auch noch durch einen "sage"- oder "denke"-Block ausgeben lassen. Oftmals ändern sich Variableninhalte aber so schnell, dass man die Zwischenschritte, in denen sich dann vielleicht das Problem verbirgt, mit den Augen nicht wahrnehmen kann. An dieser Stelle wäre es hilfreich, diese Zwischenschritte irgendwo aufzeichnen zu können, aber Scratch kann keine Dateien erstellen und beschreiben.

An dieser Stelle kommen die Listen ins Spiel. Statt einer Log-Datei verwenden wir einfach eine Log-Liste. Erzeuge dazu eine für alle Figuren geltende Liste und gib ihr den Namen Log (dies ist aber nur ein naheliegendes und im weiteren Text verwendetes Beispiel).

Sorge zuerst dafür, dass diese Liste beim Programmstart vollständig geleert wird.

Wenn die grüne Flagge angeklickt
lösche (alles v) aus [Log v]

Baue dann an den zur Nachverfolgung des Programmablaufs geeigneten Stellen Blöcke zur Einfügung eines Textes in die Liste ein.

Hier ein paar Beispiele, die natürlich auf die speziellen Gegebenheiten deines Projekts angepasst werden müssen:

füge [Eintritt in Skript 'Prüfe Treffer'] zu [Log v] hinzu

füge (verbinde [Sprite1-Prüfe Treffer-Schleife i= ] und (verbinde (i) und (verbinde [ - Wert von x: ] und (x)))) zu [Log v] hinzu

füge [Skript 'Prüfe Treffer' verlassen] zu [Log v] hinzu

füge [Eintritt in 'falls'-Abschnitt] zu [Log v] hinzu

füge [Eintritt in 'sonst'-Abschnitt] zu [Log v] hinzu

füge (verbinde [Klon ] und (verbinde (myID) und (verbinde [ x-Position:  ] und (x-Position)))) zu [Log v] hinzu

Der in die Liste einzutragende Text kann dabei ruhig etwas ausführlicher sein. Der Sinn besteht ja darin, dass du hinterher den Programmablauf anhand der Einträge in der Log-Liste so detailliert nachvollziehen kannst, dass sich die fehlerhafte Stelle möglichst genau auspüren lässt.

Sobald du einen Fehler im Programmablauf bemerkst, brichst du das Programm ab und schaust in die letzten Eintragungen der Log-Liste.

Die richtigen Stellen für Log-Ausgaben wählen

Für die Anzahl der im Code unterzubringenden Log-Ausgaben sollte man immer folgende Regel im Blick behalten:

So viele Ausgaben wie nötig (um das Problem zu finden), aber so wenige wie möglich (um nicht den Überblick zu verlieren)!

Log-Listen können nämlich sehr schnell anwachsen, vor allem dann, wenn die Eintragungen aus fortwährend wiederholten Schleifen heraus erzeugt werden, oder wenn sehr viele Klone parallel Eintragungen vornehmen. Je nach Anzahl der Log-Augaben, deren Positionen innerhalb der Skripte, der betroffenen Klonanzahl und der verstrichenen Laufzeit können somit innerhalb weniger Sekunden hunderttausende Zeilen in die Log-Liste geschrieben werden. Die Ausgabeanzahl pro Sekunde sollte sich in Grenzen halten, sonst verliert man leicht den Überblick, wo man überhaupt nach den wichtigen Einträgen suchen sollte.

  • Beispiel 1: Tritt ein Fehler nach 20 Sekunden Laufzeit ein und bricht man das Programm dann nach 22 Sekunden ab, kann man bei einer inzwischen 2.000 Eintragungen enthaltenen Liste noch sehr gut abschätzen, in welchem Bereich man einen genaueren Blick auf die Listenelemente werfen sollte.
  • Beispiel 2: Tritt ein Fehler aber bereits nach 5 Sekunden ein und bricht man das Programm nach 7 Sekunden ab, kann man in einer dann vielleicht bereits 100.000 Eintragungen enthaltenden Liste den zu untersuchenden Bereich nur sehr schwer eingrenzen.

Größe der Log-Liste reduzieren

Tritt ein Fehler erst nach einiger Zeit oder in bestimmten Situationen auf, ist es oft nicht notwendig, alle seit dem Programmstart erzeugten Einträge der Log-Liste aufzuheben. Es kann deshalb hilfreich sein, fortwährend für eine Verkleinerung der Liste zu sorgen und diese damit überschaubar zu halten. Dazu erweitern wir das obige Skript, in dem wir beim Programmstart für eine vollständige Löschung der Log-Liste gesorgt haben, um ein ein paar weitere Zeilen:

Wenn die grüne Flagge angeklickt
lösche (alles v) aus [Log v]
wiederhole fortlaufend 
  falls <(Länge von [Log v] :: list) > [10000]>, dann 
    wiederhole (100) mal 
      lösche (1 v) aus [Log v]
    end
  end
end

Die Schleife prüft fortlaufend, ob die Länge der Log-Liste die Anzahl 10.000 überschreitet und entfernt dann gleich mal die ältesten 100 Eintragungen am Stück, indem 100 mal das erste Listenelement gelöscht wird. Dass durch diese Abfrage am Ende gar nicht 10.000, sondern nur 9900 Eintragungen übrig bleiben, ist bei diesen Größenordnungen nicht wirklich von Bedeutung. Auf die hundert mehr oder weniger kommt es nicht an. Es geht nur darum, die Listenlänge auf einem überschaubaren Maß zu halten.

Auch hier ist es sicher nötig, ein wenig mit den Zahlen zu experimentieren, da es ganz davon abhängt, wie schnell die Liste während des normalen Programmablaufs wächst. Entstehen pro Sekunde 5.000 Eintragungen, sollte man in der Liste mehr Elemente aufbewahren, da die letzten 10.000 sonst zu wenige sein könnten, um den Fehler aufzuspüren. Zum anderen muss man dann die Anzahl der zu löschenden Elemente pro Durchgang erhöhen, sonst wächst die Liste wiederum extrem schnell an und diese Schleife kommt mit dem Löschen nicht nach.

Fazit

Eine Log-Liste ist zwar einfach anzulegen und zu befüllen, aber es erfordert ein wenig Erfahrung, um sie sinnvoll nutzen zu können. Zu viele überflüssige Log-Einträge an unwichtigen Stellen sind genau so nutzlos, wie zu wenige hilfreiche. Auch die Größenbeschränkung muss immer auf die tatsächlichen Gegebenheiten des aktuellen Projekts sinnvoll angepasst werden. Letztlich stellt aber die detaillierte Nachverfolgung des Programmablaufs und der Variablenänderungen anhand einer Log-Liste in manchen Fehlerfällen die entscheidende und manchmal auch einzige Möglichkeit dar, um das Projekt erfolgreich zu debuggen.



Code zum Einbinden ins Forum:
[wiki=de:Listen als Log-Dateien verwenden]Listen als Log-Dateien verwenden[/wiki]
Cookies helfen uns bei der Bereitstellung von Das deutschsprachige Scratch-Wiki. Durch die Nutzung von Das deutschsprachige Scratch-Wiki erklärst du dich damit einverstanden, dass wir Cookies speichern.