又是编程基础系列,本次主题是R的程序控制。掌握程序控制能力是学习系统性的构建程序的重要过程。在我看来,程序控制能力的体现在编程者能够掌握程序运行中的错误、识别低效率环节、监控程序状态、增强程序稳定性、关键点控制,这些能力跟程序本身的功能无关,但对提升工作效率及降低错误概率很有帮助。本次简单介绍一些R程序控制基础技能,以及一些我的看法。
写log
如果你的程序有非常多的循环,甚至是循环嵌套,那记录每次循环的程序状态就非常有必要,在R语言里tryCatch基本能满足这方面的需求。以我来说,一个tryCatch的结构我通常会写成这个样子:
> for(i in 1:10){
+ tryCatch({
+ fread(i)
+ },
+ warning = function(w) {write(paste(i,w), file = 'log', append = T)},
+ error = function(e) {write(paste(i,e), file = 'log', append = T)},
+ finally = {next})
+ }
>
> readLines('log')
[1] "1 Error in fread(i): input= must be a single character string containing a file name, a system command containing at least one space, a URL starting 'http[s]://', 'ftp[s]://' or 'file://', or, the input data itself containing at least one n or r"
[2] ""
[3] "2 Error in fread(i): input= must be a single character string containing a file name, a system command containing at least one space, a URL starting 'http[s]://', 'ftp[s]://' or 'file://', or, the input data itself containing at least one n or r"
[4] ""
......
工作原理很简单,就是将错误跟警告写入一个文本。可以理解为tryCatch会将程序的错误及警告分别存成对象w及e,可以用函数解析并输出对象。写log是个好习惯,试想一下你跑了好几天的程序突然出错,但却不知道错误在哪的那种恐惧......
循环及条件语句
各人觉得日常处理数据的话,熟练for循环、while循环、if else语句就能cover大部分需求。善用if els能增加程序的稳定性,例如:
> fun<-function(x){
+ x+10
+ }
>
> input<-'A'
>
> if(is.character(input)) {
+ print('输入是字符,无法计算')
+ } else {
+ fun(input)
+ }
[1] "输入是字符,无法计算"
这些函数用法过于基础,不再赘述。
检查低效率环节
基本都的方法就是用system.time包裹程序主体,例如:
> i<-1
>
> system.time({
+ while(i < 10^7) {
+ i<-i+1
+ }
+ })
用户 系统 流逝
1.50 0.01 1.55
但如果程序很复杂,强烈推荐用profvis包,展示一个简单的例子:
> i<-1
> j<-1
>
> profvis::profvis({
+ while(i < 10^7) {
+ i<-i+1
+ }
+
+ while(j < 10^7) {
+ j<-j+10
+ }
+
+ })
上面这段代码运行之后,在Rstudio的source界面(脚本界面)会展示一个profile:
每一行代码的运行时间及内存消耗一目了然。
交互式程序控制
想象一个情景,你编写了一个程序,这个程序有一个步骤涉及到数据删除,但你不是很肯定会不会误删数据,这时候可以在关键点加一个if else及gWidgets包的ginput函数,例如:
> library(gWidgets)
> library(tcltk)
> library(gWidgetstcltk)
>
> options(guiToolkit="tcltk")
>
> dataname<-'毕业论文.docx'
> file.create(dataname)
[1] TRUE
>
> file.exists(dataname)
[1] TRUE
>
> remove_check<-function(dataname) {
+ Lock1<-ginput(message = paste('你要删除',dataname,'吗? (y/n)'),
+ icon = 'warning')
+
+ if(Lock1 == 'y') {
+ Lock2<-ginput(message = paste('再确认一次,你真的要删除',dataname,'吗? (y/n)'),
+ icon = 'warning')
+ } else {
+ Lock2<-'n'
+ }
+
+ if(Lock1 == 'y' & Lock2 == 'y') {
+ Lock3<-ginput(message = paste(dataname,'删除后将无法恢复,你真的要删除吗? (y/n)'),
+ icon = 'warning')
+ } else {
+ Lock3<-'n'
+ }
+
+ return(c(Lock1,Lock2,Lock3))
+ }
>
> txt<-remove_check(dataname)
>
> if('n' %in% txt) {
+ print('论文:感谢不杀之恩')
+ } else {
+ file.remove(dataname)
+ }
运行txt<-remove_check的时候,会接连弹出窗口:
需要注意的是,上面这个弹窗啥都不输入且点击OK,则会传回"";点击右上角x会传回字符串的FALSE;点击Cancel会传回NA。
这里我输入的顺序是yyn,接着程序才会继续运行后面的if else。
> if('n' %in% txt | 'FALSE' %in% txt) {
+ print('论文:感谢不杀之恩')
+ } else {
+ file.remove(dataname)
+ }
[1] "论文:感谢不杀之恩"
写在后面
这个基础系列差不多就到这,这些是我过去日常编程中挑选出来我觉得比较实用的知识点。接下来会继续探索R的更多工具,感谢各位持续关注。