############################################################################### ## Inhalt: ## Kapitel 3 (Debugging) des R-Kurses von Ruckdeschel & Kohl ############################################################################### ############################################################################### ## 3.4. Debugging ############################################################################### ## Generell gibt es zwei Möglichkeiten: ## 1. Eine Programm ist bereits 'abgestürzt'; d.h., mit einem Fehler ## abgebrochen worden. ## 2. Es gibt ein im Code eingefügtes Signal, welches eine Inspektion ## des Codes ermöglicht. ## Während der Testphase eines neuen Programms, empfiehlt es sich u.U., ## dass auch Warnungen zu einem Programmabbruch führen. Dies kann erreicht ## werden, indem man die Option 'warn' auf einen Wert >= 2 setzt. ## Dann führt eine Warnung ebenfalls zum Programmabbruch. ?options ## siehe 'warn' ## also: options(warn = 2) ## Achtung: Sollte nicht nur das Programm, sondern sogar der gesamte R-Prozess ## abstürzen, ist es schwierig den auslösenden Fehler zu finden. ########################################################### ## Welche Möglichkeiten gibt es, nach einem Absturz (des Programms) ## festzustellen, was die Ursache dafür war? ## Hierfür ist die Funktion 'traceback' vorgesehen. ## Bemerkung: ## Eigentlich sollte dies natürlich aus der Fehlermeldung hervorgehen. ## Bei (eigenen) Programmen, die noch in der Testphase sind, ist dies ## jedoch i.d.R. nicht der Fall. ## ein einfaches Beispiel plot(1:10, 1:8) traceback() ## In einer komplexeren Situation hilfreich ?dump.frames ?debugger ## Abspeichern und späteres Debuggen ## Für weitere Einzelheiten siehe auch ?condition ########################################################### ## In der Situation, dass Sie eigene Programme entwickeln, ## stellt 'traceback' eine erste Möglichkeit dar, herauszufinden, ## in welchem Subprogramm/Subfunktion der Fehler auftrat. ## Ausgehend davon wird man die Fehler dann näher untersuchen. ########################################################### ########################################################### ## 1. Möglichkeit: cat und print ########################################################### ## Das könnte man auch als 'poor man's debugging' bezeichnen. ## Man fügt in ein bestehendes Progamm cat oder print ## Befehle ein, mit denen dann Zwischenergebnisse oder gewisse ## Variablen ausgegeben werden. ## In vielen Fällen reicht dies bereits aus, um den Fehler zu finden. ####################################### ## Wir verwenden das Beispiel der Fibonacci-Zahlen aus der Übung (Blatt 3) ## Zur Kontrolle der einzelnen Berechnungsschritte bauen wir ## eine zusätzliche 'print'-Anweisung ein. fibonacci <- function(n){ if(n == 0) return(0) if(n == 1) return(c(0, 1)) ## Verwendung einer for-Schleife an <- numeric(n+1) an[2] <- 1 for(i in 3:(n+1)){ an[i] <- an[i-2] + an[i-1] print(an) ## ergänzt zum 'debugging' } return(an) } fibonacci(5) ## Wir überprüfen zusätzlich den Laufindex fibonacci <- function(n, method = 'Schleife'){ if(n == 0) return(0) if(n == 1) return(c(0, 1)) ## Verwendung einer for-Schleife an <- numeric(n+1) an[2] <- 1 for(i in 3:(n+1)){ cat('Iteration:\t', i, '\n') ## ergänzt zum 'debugging' an[i] <- an[i-2] + an[i-1] cat('Ergebnis der Berechnungen:\t', an, '\n') ## ergänzt zum 'debugging' } return(an) } fibonacci(5) ## jetzt wieder ohne zusätzliche Ausgaben fibonacci <- function(n){ if(n == 0) return(0) if(n == 1) return(c(0, 1)) ## Verwendung einer for-Schleife an <- numeric(n+1) an[2] <- 1 for(i in 3:(n+1)){ an[i] <- an[i-2] + an[i-1] } return(an) } ########################################################### ## 2. Möglichkeit: debug (und undebug) ########################################################### ## Wird 'debug' mit dem Namen einer Funktion aufgerufen, ## so wird anschließend bei jedem Aufruf dieser Funktion der ## Debugging-Modus aktiviert. debug('fibonacci') fibonacci(5) ## Achtung: Will man sich die Variable 'n' ansehen, so muss ## man diese explizit mit print(n) ausgeben, da 'n' im ## Debugger-Modus auf für 'next' also nächster Ausdruck ## steht. ## Das Debugging verlassen kann man durch Eingabe von Q ## Damit bleibt für die ausgewählte Funktion aber nach wie vo ## der Debugging-Status bestehen fibonacci(5) ## Um den Debugging-Status aufzuheben, muss man die Funktion ## 'undebug' mit dem Namen der Funktion aufrufen. undebug('fibonacci') ########################################################### ## 3. Möglichkeit: browser ########################################################### ## Die Funktion browser() ist dafür vorgesehen, in eigene Programme ## integriert zu werden. Wird bei der Ausführung des Programms dann ## die Stelle erreicht, an der die Anweisung 'browser()' steht, so ## wird die weitere Ausführung des Programms unterbrochen und ## der Debugger-Modus aktiviert fibonacci <- function(n){ if(n == 0) return(0) if(n == 1) return(c(0, 1)) ## Verwendung einer for-Schleife an <- numeric(n+1) an[2] <- 1 for(i in 3:(n+1)){ an[i] <- an[i-2] + an[i-1] browser() } return(an) } fibonacci(5) ########################################################### ## 4. Möglichkeit: trace ########################################################### ## Was machen Sie jedoch, wenn es sich bei dem Programm, das Sie untersuchen ## möchten, nicht um Ihr eigenes Programm handelt? ## 1. Möglichkeit: ## R ist freie Software und man kann sich jederzeit den Source-Code aller ## Programme besorgen. Diesen Code könnte man dann entsprechend abändern ## und browser-Befehle integrieren. ## 2. Möglichkeit: ## Sie verwenden die Funktion 'trace' fibonacci <- function(n){ if(n == 0) return(0) if(n == 1) return(c(0, 1)) ## Verwendung einer for-Schleife an <- numeric(n+1) an[2] <- 1 for(i in 3:(n+1)){ an[i] <- an[i-2] + an[i-1] } return(an) } as.list(body(fibonacci)) ## Hilfreich bzw. u.U. nötig, Verwendung von quote trace(what = 'fibonacci', tracer = browser, at = 6) ## trace(what = 'fibonacci', tracer = quote(print(an)), at = 6) body(fibonacci) ## In tracer wird eine Funktion bzw. ein nicht ausgewerteter Ausdruck ## - z.B. mit Hilfe von 'quote' erzeugbar - an trace übergeben, ## unmittelbar vor dem Schritt, der in 'at' festgelegt ist ## ausgewertet wird. ## oder ## Typischer Weise wird die Funktion 'browser' eingeschleust... trace(what = 'fibonacci', tracer = browser, exit = browser) body(fibonacci) test <- function(x){ n <- trunc(x) fibonacci(n) } test(4.1) ## Um 'trace' aufzuheben, muss die Funktion 'untrace' aufgerufen werden untrace('fibonacci') ########################################################### ## Eine weitere wichtige Funktion im Zusammenhang mit dem Abfangen von ## Fehlern ist die Funktion 'try'. ## Sie versuchen also eine Funktion auszuführen, gelingt der Versuch, ## dann ist das Ergebnis von try gerade das Ergebnis dieser Funktion. ## Im Fall des Mißlingens ist das Ergebnis von try eine Fehlermeldung. res <- try(plot(1:10, 1:8)) ## zwar Fehlermeldung aber kein Abbruch res ## oder res <- try(plot(1:10, 1:8), silent = TRUE) ## keine Fehlermeldung wird ausgegeben res ## Durch Verwendung von 'try' haben Sie also die Möglichkeit innerhalb ## Ihrer Programme Fehler abzufangen und entsprechenden Alternativcode ## auszuführen.