统计211

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3192|回复: 0
打印 上一主题 下一主题

我来闲话一下R的debug经验

[复制链接]
跳转到指定楼层
1
发表于 2011-7-4 20:19:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
看到有人头疼debug,我来说一下我的经验之谈,给初学者。纯属个人抓瞎,小心被雷到!

首先说设计程序结构。我的程序一般有三个部分: 一个是Main.R,一般和数据什么的放在主工作目录。Main.R 的作用是建立一个工作环境,调用相关函数,加载额外的包,分配内存大小什么的,以及参数的输入 。 一个子目录src,下面是一堆.R文件(多少不等,我最多写过二十多个). 每个文件是一个函数,命名和用里面函数同名,便于查找。另外一个子目录util,放一些简单工具,比如有一个函数是用来递归的source一个或多个文件夹下所有的R文件。

首先调试src下每个函数,当然你写函数时候要知道你的函数输入是什么结构,输出是什么,保证给定的输入能有结果。不要把单个函数写的太复杂,一个函数一个功能。当然写的太琐碎也不好,因为总是有很大的overhead call functions会递减整个系统的性能,这个是后话了。单个函数的debug一般比较简单,一页一两百行的代码。另外友情提示,"..." 是一个很好的用法,配合Main.R使用。

到最后就是就是在主目录下source("Main.R")把所有的函数串起来,Main.R里一般我都用hard coded. 这时候正式的debug开始了。当source("Main.R")出现错了,程序停了下来,会提示一些错误,有些很浅显,比如矩阵相乘出现non-comfortable. 有些很难找到,怎么找到错误在哪里呢?这时候你可以敲一下traceback(). R就会自己recovery到出错的地方,并且1,2,3,4,5告诉在出错之前R都做了些什么。在哪个函数的哪里出错了。定位了就好处理了。

有时候会有特别的情况,比如每到某一个地方就报错NAs are not allowed.可是这里本来不应该有NA啊。这时候你可以在这个前面放置browser().每到这里,R就会暂停,提示你测试一下周围环境是不是你需要的,在这里你也可以给一些其他的输入。比如每次我输入a<-5这里就出现NA,我可以零时输入a<- 6看看什么结果。不过具体情况具体分析。browser还可以在出错时停止,所以就不会每次有错没错都停在那里等你。还有一些东西也是很有用,比如R默认在运行中会把所有的Warnings保存下来,最后程序结束了才提示超过50的警告,不过黄花菜也凉了,可以在程序运行前加一句options(warn=1),你可以写在Main.R的第二行,(第一行写rm(list=ls()))这样一旦有警告,就会print出来,这样心中有数了。

有时候程序既没有报错,又没有警告,但是出来的东西不是我们想要的,大概里边某个地方算错了,我记得我有一次把seq写成rep了。这时traceback()用不了了,没有错啊!browser()有时候比较烦人,每次都停。这回时候可以试着零时用一下全局变量(<<-),我是强烈反对在函数里用全局变量了。你可以把中间你比较怀疑的某些值设成全局变量,当程序运行结束了,你可以单独对付,我的感觉是这样见效很快。不过记得结束后还原。

我还用过一个比较二的方法找错。前提是前面都失效了,这个问题出现在我做1000次循环,程序死在了第993次上,纯属偶然,这也得找出来啊。于是我就在程序从程序开始到后来每隔一段print(p),其中p是第几个print。假设我有10个p打印出来,这样,我开始运行时,屏幕上会从打印1,2,3,4,5,6,7,8,9,10,1,2,3,4,5...直到出错了,查看最后一个打印出来的是什么,就知道大概哪个附近有问题了。其实print有很多很好的用途 比如你做一个100000次的循环,大概需要两个小时,在代码运行时你想知道当前是第几次,你可以每隔1000次pint一下当前是第几次。如果能配合proc.time()使用,你还能做一个进度条估计剩余时间。都是很简单的东西,但是很有趣。

补充一下,try() 和tryCatch()是两个很好的处理error的函数。用法很简单,参看帮助。

总结一下. debug是一个比较系统的过程,不是最后那一两次调试才叫作debug.基于“出来混的,终究是要还的”,我还是比较建议从一开始写良好的编程习惯,这样有利于检查问题。比如在写之前就声明输入的类型,N x P的矩阵还是 P x 1的,矩阵还是其他的,输出是什么东西。不要一边写一边想加个张三就加个张三,想加个李四就加个李四,这样会越来越乱。。变量命名不要嫌长,最好好能说明功能,比如 KStepNewtonDimMov for "K-step Newton Dimension Move". 注意代码缩进。for loop如果很长,超过一个屏幕,应该写成这样:
for(i in 1:100)
{
  ## long code
} # for (i in 1:100)
个人经验,这些可以对付一般的问题了,其他的只能google了。更多的大家可以help.search("debug")。

评分

参与人数 1统计币 +6 收起 理由
veil + 6

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 转播转播 分享分享 分享淘帖 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


免责声明|关于我们|小黑屋|联系我们|赞助我们|统计211 ( 闽ICP备09019626号  

GMT+8, 2025-4-19 20:59 , Processed in 0.077476 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表