Norman Matloff 和PeteSalzman在其著作《TheArt of Debugging, withGDB,DDD,Eclipse》中曾说过,确认原则是调试的本质。程序员编写程序是为实现特定目的,而一个程序可以由许多目的组成,调试是确认某些目的是否达到了,如果未能达到目的,那么便可通过在调试中查看变量,发现问题症结,进而解决问题。
在R中进行debug有几种不同方式,你如果使用诸如Rstudio等图形软件,调试代码很容易,所有的调试都在图形界面下完成,你只需要根据需求在图形界面下点击相应选项来进行断点设置、单步执行、查看变量等操作,查找问题症结。但如果在命令行界面下调试R代码,那就得要借助于一些特别的调试工具。R的基础软件包base中包含了一些基本的调试工具,当然CRAN中也有一些其它优秀的调试工具。
对于一般用户来说,掌握base中的基本调试工具就能满足大部分需求,下面介绍R基本软件包base中的调试工具的使用方法(也包含setBreakpoint(),其位于utils包中)。
由于在启动和关闭调试中需要用到部分调试命令,这里就先介绍进入调试模式后需要用到的一些基本命令。
1.基本调试命令
在进入dedbug调试状态后,命令提示符从>变为Browse[d]>(d表示函数调用链的深度),可以通过一些基本的命令来进行控制:
- n(表示next): 告诉R执行下一行代码,并且执行完后马上暂停,实际就是一行一行地执行代码。相当于C语言开发工具Turbo C中的trace into。
- c(表示continue):表示会执行若干条语句。若当前处在循环中,这一步会执行完整个循环,若当前处在函数内但又不再循环中,则会执行完当前函数。相当于C语言开发工具Turbo C中的step over。
- where: 输出一份栈跟踪路径,显示到达当前位置的过程中函数的调用序列。
- Q: 退出brower,返回R的主交互模式。
-
任意R命令: 即使在调试状态browser中,依然处于R的交互模式中,所以你可以用任意R命令。
2启动和关闭调试
R的核心调试工具由browser构成,通过browser,你可以逐行运行代码,并在运行过程中进行检查,查看变量。在调试代码时,我们首先要让程序进入调试状态,有下列几种方式可以实现。
2.1 在代码中的指定位置加入browser()
开启调试:
在R源文件中的指定位置插入函数browser(),保存源文件,运行源程序,程序一旦运行到browser()处,将会自动进入debug状态。
取消调试:
但当用户完成调试后,需要手动删除源文件中的browser()函数,否则每次运行到browser()位置都会进入debug状态。
temp_test.R:
(PS:此文件在下面会多次用到,所以贴出来,但后续用到时已经删除了browser())
countsum <- function(count)运行之后:
{
sum <- 0
for (i in 1:count)
{
sum <- sum + i
}
browser()
return (sum)
}
results <- countsum(100)
print(results)
> source("temp_test.R")
Called from: countsum(100)
Browse[1]>
2.2调用debug()
R的调试工具是针对单个函数的,由于拥有函数式编程的特性,R的每一个运算符,实际上也是函数(关于R函数,可参考这里),这里所说的函数不包括一般的运算符。
开启调试:
执行命令debug(fun)
fun指函数名,这样每次调用函数fun()都会进入调试状态。
取消调试:
执行命令undebug(fun)
再次调用函数fun()将不会进入调试状态。
如下例所示:
> source("temp_test.R")
[1] 5050
> debug(countsum)
> countsum(10)
debugging in: countsum(10)
debug at temp_test.R#1: {
sum <- 0
for (i in 1:count) {
sum <- sum + i
}
return(sum)
}
Browse[2]> n
debug at temp_test.R#2: sum <- 0
Browse[2]> n
debug at temp_test.R#3: for (i in 1:count) {
sum <- sum + i
}
Browse[2]> sum
[1] 0
Browse[2]> c
exiting from: countsum(10)
[1] 55
2.3调用debugonce()
同debug()的调用方式一样,区别在于:debugonce()只会在设置之后的第一次调用时进入调试状态且只只进入一次,而debug()可以进入无限多次直到通过undebug()取消调试。
2.4用函数trace()进行跟踪
trace(fun,tracer)
fun表示需要跟踪或者取消跟踪的函数名;racer表示跟踪的对象,可以是某个函数,也可以是函数中的某个表达式。
开启调试:
执行命令trace(fun,tracer)
每次调用函数fun(),都会显示表达式的值,或者对函数进行某些操作。
取消调试:
执行命令untrace(fun)
取消对某个函数的跟踪。
如下例所示:
> source("temp_test.R")
[1] 5050
> trace(countsum,sum)
[1] "countsum"
> countsum(10)
Tracing countsum(10) on entry
[1] 55
> untrace(countsum)
> trace(countsum,browser())
Called from:methods::.TraceWithMethods(countsum, browser(), where =<environment>)
Browse[1]> Q
> rm(list = ls())
> source("temp_test.R")
[1] 5050
> trace(countsum,sum)
[1] "countsum"
> countsum(10)
Tracing countsum(10) on entry
[1] 55
> untrace(countsum)
> trace(countsum,browser)
[1] "countsum"
> countsum(20)
Tracing countsum(20) on entry
Called from: eval(expr, envir, enclos)
Browse[1]> n
debug: {
sum <- 0
for (i in 1:count) {
sum <- sum + i
}
return(sum)
}
Browse[2]> c
[1] 210
2.5.设置断点setBreakpoint()
setBreakpoint()位于utils包中,R版本需要>=2.10
setBreakpoint(filename,linenumber)
表示会在源文件filename的第linenumber行设置断点,但是实际上是通过函数来进行设置的,这点需要注意,即只有所设置的断点处于文件中的某个函数内才是有效的。此函数可以用在debug调试状态中,在单步调试过程中,当设置了断点后,可以让程序直接运行到到断点处,这在调试过程中很有用处。
开启调试:
执行命令setBreakpoint(filename,linenumber)
然后再执行代码,当代码运行到断点处即进入调试状态。
取消调试:
执行命令untrace(fun)
fun表示函数名,Breakpoint要设在函数内才有效,所以应当通过函数来取消断点,setBreakpoint()是通过调用trace()发挥作用的。
如下例所示:
>source("temp_test.R")
[1]5050
>setBreakpoint("temp_test.R",3)
/home/sheng/WinD/test/temp_test.R#3:
countsumstep 3 in <environment: R_GlobalEnv>
>countsum(20)
temp_test.R#3
Calledfrom: countsum(20)
Browse[1]>n
debug:for (i in 1:count) {
sum<- sum + i
}
Browse[2]>where
where1: countsum(20)
Browse[2]>sum
[1]0
Browse[2]>c
[1]210
参考:
[1] Norman Matloff著,陈堰平等译.R语言编程艺术.机械工业出版社,2013-05