又是编程基础系列,本次主题是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:




r语言中readHTMLTable R语言中主要控制结构有_r语言中readHTMLTable


每一行代码的运行时间及内存消耗一目了然。

交互式程序控制

想象一个情景,你编写了一个程序,这个程序有一个步骤涉及到数据删除,但你不是很肯定会不会误删数据,这时候可以在关键点加一个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的时候,会接连弹出窗口:


r语言中readHTMLTable R语言中主要控制结构有_误删_02


r语言中readHTMLTable R语言中主要控制结构有_r library car_03


r语言中readHTMLTable R语言中主要控制结构有_误删_04


需要注意的是,上面这个弹窗啥都不输入且点击OK,则会传回"";点击右上角x会传回字符串的FALSE;点击Cancel会传回NA。

这里我输入的顺序是yyn,接着程序才会继续运行后面的if else。


> if('n' %in% txt | 'FALSE' %in% txt) {
+   print('论文:感谢不杀之恩')
+ } else {
+   file.remove(dataname) 
+ }
[1] "论文:感谢不杀之恩"


写在后面

这个基础系列差不多就到这,这些是我过去日常编程中挑选出来我觉得比较实用的知识点。接下来会继续探索R的更多工具,感谢各位持续关注。