In der Blog-Reihe „Fehlerbehandlung in R“ geht es um effizientes und systematisches Überprüfen von R-Code. Den Beginn macht das Finden von Fehlern durch Debugging, weiter geht es mit der Handhabung von Fehlern und endet mit Unit-Testing, das zum Überprüfen von korrekter Funktionalität von R-Code dient.
Die Reihe startet mit Debugging in R, wobei Debugging ein breitgefächertes Thema ist. Dieser Artikel fokussiert sich daher auf die Möglichkeiten, die RStudio bietet.
Debugging hilft dabei herauszufinden an welcher Stelle im Code sich ein Fehler befindet oder an welcher Stelle der Code sich anders verhält als erwartet. Dies beinhaltet im Generellen drei Schritte:
- den Code laufen lassen
- Code an der Stelle stoppen an welcher vermutet wird, dass sich dort der Fehler befindet
- Schritt für Schritt durch den Code gehen und diesen dabei überprüfen.
In den Debugging-Modus eintreten
Um in den Debugging-Modus zu kommen, muss RStudio mitgeteilt werden, wann es die Berechnungen stoppen soll. Es gibt keinen „Pause-Button“ mit welchem man eine laufende Berechnung stoppen kann um in den Debugging-Modus einzutreten. Dies ist in so gut wie allen anderen Programmiersprachen der Fall, da die Berechnungen in der Regel zu schnell von statten gehen als dass es möglich wäre an der richtigen Stelle zu stoppen. Stattdessen bestimmt man zuvor an welcher Stelle der Code angehalten werden soll. Dies ist nicht zu verwechseln mit dem “Stop”-Button über der der Konsole, welcher die Berechnung komplett abbricht.
Vor einer Zeile stoppen
Der einfachste und meist genutzte Weg um in den Debugging-Modus zu gelangen, ist es einen Breakpoint im Code-Editor zu setzen. Dies kann auf einfache Weise in RStudio gemacht werden indem man links neben die Zeilennummer klickt oder durch das Drücken von Shift+F9 auf der Tastatur und zeitgleiches Klicken mit der Maus in der gewünschten Zeile.
Hierbei wird eine Tracing-Funktion innerhalb der eigentlichen Funktion eingefügt. Der Breakpoint wird durch einen ausgefüllten roten Kreis im Editor gekennzeichnet. Außerdem kann man einen schnellen Überblick erlangen in welcher Funktion sich ein Breakpoint befindet, indem man in das Environment-Fenster schaut, welche Funktion ebenfalls durch einen roten Kreis gekennzeichnet wird.
Falls die Funktion noch nicht existiert, zu einem weil man das File noch nicht gesourced hat oder zu anderem weil sich die Funktion im Environment und im Editor unterscheiden, kann der Breakpoint noch nicht aktiviert werden. Dies wird durch einen nicht-ausgefüllten Kreis kenntlich gemacht.
In der Regel hilft es das File einmal zu sourcen, wodurch die Tracking-Funktion eingefügt wird und der Breakpoint startbereit ist.
Setzt man den Breakpoint per RStudio-Editor ist es nicht notwendig die Funktion zu bearbeiten und zusätzlichen Code per Hand einzufügen. Allerdings gibt es bestimmte Situationen in denen diese Breakpoints nicht funktionieren wie beispielsweise komplexere Funktionssyntaxen. Außerdem wird konditionelles Debugging bisher nicht von RStudio unterstützt. Hier schafft die browser()
-Funktion Abhilfe. Da es sich hierbei um eine tatsächliche Funktion handelt, muss sie in den Code geschrieben werden, kann aber an so gut wie jeder Stelle hinzugefügt werden. Sind sie erstmal aktiv und aufgerufen verhalten sich die Editor-Breakpoins und browser()
sehr ähnlich.
Stoppen bevor eine Funktion ausgeführt wird
Der Editor-Breakpoint oder browser()
eigenen sich optimal für Funktionen für welche der Source-Code vorliegt. Hat man jedoch nicht das .R File zur Hand kann alternativ eine ganze Funktion mit debug()
bzw. debugonce()
gedebugged werden. Hierfür wird die jeweilige Funktion innerhalb von debug()
bzw. debugonce()
geschrieben , beispielsweise debugonce(mean)
. Dies ändert nicht die Funktion an sich, aber es startet den Debugger direkt nach dem Funktionsaufruf, man kann es sich vorstellen als würde man einen Breakpoint direkt zu Beginn der Funktion setzen würde. debugonce()
aktiviert den Debug-Modus nur ein einziges Mal für die jeweilige Funktion zum nächstmöglichen Zeitpunkt zu welchem diese aufgerufen wird. debug()
hingegen aktiviert jedes Mal den Debugger, wenn die Funktion aufgerufen wird, was schlimmsten Falls in einer endlosen Schleife resultieren kann. Daher ist es in der Regel zu empfehlen debugonce()
zu benutzen. Das Gegenstück zu debug()
ist undebug()
, welches benutzt wird wenn man nicht mehr jedes Mal die Funktion bei Aufruf debuggen möchte.
Bei Fehler stoppen
Die dritte Möglichkeit in den Debugging-Modus zu gelangen ist die Einstellung, dass der Debugger jedes Mal automatisch aktiviert wird, wenn ein Fehler auftaucht. Dadurch stoppt die Funktion automatisch und der Debugging-Modus startet direkt von selbst. Diese Funktionalität wird über die RStudio Oberfläche aktiviert in dem man Debug -> On Error von „Error Inspector“ zu „Break in Code“ ändert.
Allerdings wird der Debugger per Default nur aktiviert, wenn ein Fehler im eigenen Code auftaucht. Falls man einen Fehler finden möchte, welcher ebenfalls Code von Dritten beinhaltet, kann diese Einstellung unter Tools -> Global Options und dem abwählen von „Use debug error handler only when my code contains errors“ abgeändert werden. Alternativ kann die Option dauerhaft mit options(error = browser())
überschrieben werden. Es kann jedoch schnell störend werden, dass jedes Mal der Debugger aktiviert wird. Daher sollte nicht vergessen werden diese Option wieder mit options(error = NULL)
rückgängig zu machen sobald das Debugging beendet ist.
Der nächste Teil der Reihe „Fehlerbehandlung in R“ dreht sich um effektives Debugging in R nachdem der Debugger aktiviert wurde.