天天看點

R語言-調試代碼

和其他語言一樣你自然可以通過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()
: fifth() at #1
: fourth() at #1
: third() at #1
: second() at #1
: first()
           

一般來說,越靠近上層的函數出錯的可能性越大,當然這不是絕對的,比如有可能是fourth函數錯誤的使用了fifth函數才導緻最終的報錯。

而且這個調用棧可以展示函數運作的過程,你可以檢查一下這和你的邏輯是否有沖突。

這個工作在RStudio中顯得更簡單,每當程式報錯RStudio都會出現下面的一個灰色方框:

R語言-調試代碼

選擇第一個Show Traceback選項即可檢視調用棧資訊,而且會一直對應該錯誤,而不會想直接調用traceback函數一樣顯示最近出錯代碼的資訊。

R語言-調試代碼

當你已經找到錯誤的函數,接下來就該使用browser函數檢查一下這個函數的具體運作細節了

browser

利用browser函數可以讓R在運作過程中暫停,使你得以在指令行中鍵入指令。此時,你的任何指令的活動環境已經不再是全局環境,而改為了處于暫停狀态下的函數的運作環境,是以你可以直接檢視函數體内部的各種對象取值,并在同一環境下運作一些測試代碼。

要是用browser隻需将browser()放入所需的函數體中,并将該函數儲存即可,下次運作時便會在browser()處停下來。

> browser_test<-function(){
+   t1<-
+   browser()
+   t2<-
+ }
> browser_test()
Called from: browser_test()
Browse[]> print(t1)  #指令提示符變為Browse[1]>
[] 
           

此時,我們處于一個新的R模式,浏覽器模式(browser mode)。

在RStudio的右上角可以跟直覺地檢視目前環境中的對象,左上角則會突出browser()所暫停的代碼行

R語言-調試代碼

之後你可以利用提示符視窗上方的導航按鈕實作下一步操作:

第一個按鈕是Next(下一步) 用來運作函數的下一行代碼

第二個按鈕是Continue(繼續) 用來運作函數剩餘的所有代碼,完成之後退出浏覽器模式

第三個按鈕是Stop(停止) 他會立刻中斷并退出浏覽器模式,不運作任何代碼

R語言-調試代碼

這些按鈕的功能也可以通過在浏覽器模式的提示符視窗中鍵入n、c、Q來分别實作。那麼問題出現了,如果想檢視的對象名也是n、c或Q呢,該怎麼辦?此時直接鍵入這三個字母會優先處理三個按鈕的功能。此時應使用get()函數檢視對應對象,在浏覽器模式下,cont是c的同義詞,where會顯示整個調用棧,是以檢視以cont和where命名的對象也要使用get()函數。

斷點

R中的斷點和絕大多數IDE中相似,都是在代碼行号的左側用滑鼠單擊一下出現一個紅點,在R中即代表在該行代碼前加了browser()函數。如果是空心紅點則需要運作腳本面闆右上方的Source按鈕運作腳本,空心紅點即會變為實心。當然如果你選中了Source on Save選項,那麼每次儲存時都會自動将檔案source。

R語言-調試代碼

browser和斷點是對自定義函數調試的好工具,那麼如何對一個R中已存在的含資料進行調試呢?

答案是debug函數!

debug

debug函數用于在一個已存在的的函數的起始處“添加”一個browser()語句。此時,函數處于“調試模式”,每次隻要一運作該函數都會進入浏覽器模式,需要通過undebug函數将browser()從該函數中移出,isdebugged函數用于檢查某個函數是否處于“調試”模式

> test<-function(){
+   print("hello")
+ }
+ 
#進入"調試"模式
> debug(test)
> isdebugged(test)
[] TRUE

> test()
debugging in: test()
debug at #1: {
    print("hello")
}
Browse[]> Q

#退出調試模式
> undebug(test)
> isdebugged(test)
[] FALSE
> test()
[] "hello"
           

如果覺得這個過程很麻煩可以使用debugonce函數代替debug函數,這樣隻有第一次運作函數時會進入浏覽器模式,當調試結束之後再次運作該函數便可以正常運作。

RStudio中可以可視化實作這個功能,那就是程式報錯之後灰色框的第二個選項:Return with Debug(以調試模式重新運作),這可以實作debugonce的功能,使錯誤的指令進入一次“調試”模式。

trace

trace可以在函數體的某一行添加某一R表達式,形如

trace("test",browser,at=4)

當你除了函數名不添加其他參數時,每次運作該函數都會在指令行中顯示:

trace:<the function>

同理untrace()函數也能将函數從這個狀态中釋放出來。

> trace(print)
> print()
trace: print()
[] 

> untrace(print)
> print()
[] 
           

recover

和browser()函數一樣你可以将recover()函數放在任何你需要的地方,一旦R運作到recover函數時,它會暫停并顯示目前的調用棧資訊,而且你可以選擇進入哪一個函數進入調試模式

> first()

Enter a frame number, or  to exit   

: first()
: debug_test.R#3: second()
: #2: third()
: debug_test.R#5: fourth()
: debug_test.R#6: fifth()
           

注意這裡的顯示順序和調用棧相反

如果你覺得将recover()放入代碼中很繁瑣,可以将recover添加到R函數的全局選項中

options(error=recover)

這樣一來任何錯誤都會執行recover(),知道關閉目前R會話,或者

options(error=NULL)

繼續閱讀