和其他语言一样你自然可以通过print一些参数之类的方法进行debug,但是R或RStudio提供的一些代码调试工具还是能为你提供一些有用的尝试。
这些工具包括:traceback、browser、debug、debugonce、trace和recover函数。
一般debug包括两个步骤,首先是定位代码错误发生的位置,然后是找出代码发生错误的原因并解决
其中第一步可以借助traceback函数来完成
traceback
traceback函数可以帮助你精确定位错误。很多R函数之间都会存在互相调用的情况,如何确定出错的函数往往是个难题。
first<-function()second()
second<-function()third()
third<-function()fourth()
fourth<-function()fifth()
fifth<-function()bug()
上述函数都在调用下一个函数(除了最后一个函数)
由于bug函数不存在,运行first()将会报错 Error in bug() : could not find function "bug"
这里由于函数关系简单我们很容易就知道了错误的原因,但很多时候你根本不知道出错的函数是什么地方为什么被调用的,此时traceback()可以看到出错之前R函数调用的路径,并返回一个调用栈(call stack),即调用函数的有序列表。
> traceback()
5: fifth() at #1
4: fourth() at #1
3: third() at #1
2: second() at #1
1: first()
一般来说,越靠近上层的函数出错的可能性越大,当然这不是绝对的,比如有可能是fourth函数错误的使用了fifth函数才导致最终的报错。
而且这个调用栈可以展示函数运行的过程,你可以检查一下这和你的逻辑是否有冲突。
这个工作在RStudio中显得更简单,每当程序报错RStudio都会出现下面的一个灰色方框:
选择第一个Show Traceback选项即可查看调用栈信息,而且会一直对应该错误,而不会想直接调用traceback函数一样显示最近出错代码的信息。
当你已经找到错误的函数,接下来就该使用browser函数检查一下这个函数的具体运行细节了
browser
利用browser函数可以让R在运行过程中暂停,使你得以在命令行中键入命令。此时,你的任何命令的活动环境已经不再是全局环境,而改为了处于暂停状态下的函数的运行环境,因此你可以直接查看函数体内部的各种对象取值,并在同一环境下运行一些测试代码。
要是用browser只需将browser()放入所需的函数体中,并将该函数保存即可,下次运行时便会在browser()处停下来。
> browser_test<-function(){
+ t1<-1
+ browser()
+ t2<-2
+ }
> browser_test()
Called from: browser_test()
Browse[1]> print(t1) #命令提示符变为Browse[1]>
[1] 1
此时,我们处于一个新的R模式,浏览器模式(browser mode)。
在RStudio的右上角可以跟直观地查看当前环境中的对象,左上角则会突出browser()所暂停的代码行
之后你可以利用提示符窗口上方的导航按钮实现下一步操作:
第一个按钮是Next(下一步) 用来运行函数的下一行代码
第二个按钮是Continue(继续) 用来运行函数剩余的所有代码,完成之后退出浏览器模式
第三个按钮是Stop(停止) 他会立刻中断并退出浏览器模式,不运行任何代码
这些按钮的功能也可以通过在浏览器模式的提示符窗口中键入n、c、Q来分别实现。那么问题出现了,如果想查看的对象名也是n、c或Q呢,该怎么办?此时直接键入这三个字母会优先处理三个按钮的功能。此时应使用get()函数查看对应对象,在浏览器模式下,cont是c的同义词,where会显示整个调用栈,因此查看以cont和where命名的对象也要使用get()函数。
断点
R中的断点和绝大多数IDE中相似,都是在代码行号的左侧用鼠标单击一下出现一个红点,在R中即代表在该行代码前加了browser()函数。如果是空心红点则需要运行脚本面板右上方的Source按钮运行脚本,空心红点即会变为实心。当然如果你选中了Source on Save选项,那么每次保存时都会自动将文件source。
browser和断点是对自定义函数调试的好工具,那么如何对一个R中已存在的含数据进行调试呢?
答案是debug函数!
debug
debug函数用于在一个已存在的的函数的起始处“添加”一个browser()语句。此时,函数处于“调试模式”,每次只要一运行该函数都会进入浏览器模式,需要通过undebug函数将browser()从该函数中移出,isdebugged函数用于检查某个函数是否处于“调试”模式
> test<-function(){
+ print("hello")
+ }
+
#进入"调试"模式
> debug(test)
> isdebugged(test)
[1] TRUE
> test()
debugging in: test()
debug at #1: {
print("hello")
}
Browse[2]> Q
#退出调试模式
> undebug(test)
> isdebugged(test)
[1] FALSE
> test()
[1] "hello"
如果觉得这个过程很麻烦可以使用debugonce函数代替debug函数,这样只有第一次运行函数时会进入浏览器模式,当调试结束之后再次运行该函数便可以正常运行。
RStudio中可以可视化实现这个功能,那就是程序报错之后灰色框的第二个选项:Return with Debug(以调试模式重新运行),这可以实现debugonce的功能,使错误的命令进入一次“调试”模式。
trace
trace可以在函数体的某一行添加某一R表达式,形如 trace("test",browser,at=4)
当你除了函数名不添加其他参数时,每次运行该函数都会在命令行中显示:trace:<the function>
同理untrace()函数也能将函数从这个状态中释放出来。
> trace(print)
> print(1)
trace: print(1)
[1] 1
> untrace(print)
> print(1)
[1] 1
recover
和browser()函数一样你可以将recover()函数放在任何你需要的地方,一旦R运行到recover函数时,它会暂停并显示当前的调用栈信息,而且你可以选择进入哪一个函数进入调试模式
> first()
Enter a frame number, or 0 to exit
1: first()
2: debug_test.R#3: second()
3: #2: third()
4: debug_test.R#5: fourth()
5: debug_test.R#6: fifth()
注意这里的显示顺序和调用栈相反
如果你觉得将recover()放入代码中很繁琐,可以将recover添加到R函数的全局选项中 options(error=recover)
这样一来任何错误都会执行recover(),知道关闭当前R会话,或者options(error=NULL)