結對編程-人和代碼都長得好看系列
說實話,看本人隊友的代碼已有1年之余,也幫忙上刀山下火海不辭勞苦為她找BUG,有時找了n個小時就是因為把i達成了1,==打成了=,然而憑心而論此人代碼武功高強,內力深厚,不僅人長得漂亮而且代碼風格確實登得大雅之堂,括號該對稱就對稱,該縮進就縮進,除了註釋略微不多讓本人略感頭疼外,一切都非常...真香。
隊友采用C++編寫,在dev上跑,說起代碼,那是和本人一樣好看,比如隊友把代碼分成幾個模塊,(在鄙人強烈建議下)將其變成了工程,整個工程由5個文件組成:
- main.cpp:除了實現的主函數文件
- logIn.h:存儲登錄信息頭文件,用戶信息存於其中,可增加或修改用戶信息
- question.h:三個函數question1()、question2()、question3()分別用於生成小學,初中和高中的題目
- sign.h:三個函數sign1()、sign2()、sign3()分別用於生成小學初中高中需求的運算符
- search.h:用於查找該用戶文件夾下所有的文件
雖然sign.h,logln.h(對你沒看錯是logln不是login)模塊略微有湊工程之嫌疑,比如他們長下面這樣:
#include<iostream> #include<fstream> #include<string> #include<cstdlib> #include<ctime> #include<cstdlib> using namespace std;char sign1(int n) {//生成小學運算符 // srand((unsigned)time(NULL)); char s1[] = {‘+‘ , ‘-‘ , ‘*‘ , ‘/‘} ; return s1[n]; } string sign2(int n) {//生成初中運算符 string s2[] = { "^2" , "√"} ; return s2[n]; } string sign3(int n) {//生成高中運算符 string s3[] = { "sin" , "cos" , "tan"} ; return s3[n]; }
和:
#include<iostream> #include<fstream> #include<string> #include<cstdlib> #include<ctime> #include<cstdlib> using namespace std; int logIn(string name , string password) {//判斷用戶名和密碼是都正確,返回1為小學用戶,2為初中用戶,3為高中用戶 if((name == "張三1" || name == "張三2" || name == "張三3" )&& password == "123") return 1; else if((name == "李四1" || name == "李四2" || name == "李四3" )&& password == "123") return 2; else if((name == "王五1" || name == "王五2" || name == "王五3" )&& password == "123") return 3; else return 0; }
但是代碼從整體上看可以說是相當的規範。
好的,讓我們重新把目光放到代碼的算法部分,由於我用的python寫的,確實不知道C++寫UI的切膚之痛,所以UI部分我們暫且不論,控制臺稍後再說,我們看核心部分如何操作,生成小學題目代碼如下:
string question1() //生成小學題目 { string s = ""; char str[10]; srand((unsigned)time(NULL)); int n1 = 0 , n2 = 0 , n3 = 0; int flag = 1; int length = (rand()%4 + 2); int a[5]; char b[5]; int left[5] = {0,0,0,0,0}; //每個數左邊有幾個左括號 int right[5] = {0,0,0,0,0};//每個數右邊有幾個右括號 if(flag == 1) { for(int i = 0 ; i < length - 1; i++) { a[i] = rand()%(100 - 1 + 1) + 1;//隨機生成操作數 n1 = rand()%(4-0)+0; //隨機生成四則運算符號 b[i] = sign1(n1); } a[length - 1] = rand()%(100 - 1 + 1) + 1; b[length - 1] = ‘=‘; //最後加上等號 int exit[5] ={0,0,0,0,0}; int count = 0; //計數有幾個右括號還需要加 for(int i = 0 ; i < length ; i++) { if(rand()%3 == 2 && exit[i] > 0 && left[i] == 0 && count > 0)//一個數右邊加括號的條件 { right[i]++; exit[i]--; count--; } if(rand()%3 == 1 && i != length - 1 && right[i] == 0)//一個數左邊加括號的條件 { left[i]++; count++; for(int j = i ; j < length ; j++) exit[j]++; } } while(count != 0) //左右括號不一樣多就需要再次遍歷 { for(int i = 1; i < length ; i++) if(rand()%3 == 2 && exit[i] > 0 && left[i] == 0 && count > 0) { right[i]++; exit[i]--; count--; } } for(int i = 0 ; i < length; i++) { while(left[i] > 0) { s = s + "("; left[i]--; } s = s + itoa(a[i],str,10); while(right[i] > 0) { s = s + ")"; right[i]--; } s = s + b[i]; } } return s; }
這套算法比我的投機取巧(直接套娃式生成法,詳見彭依依博客)簡直好了太多,完全實現真隨機,首先生成操作數和數字,對一個已經生成好的,對可以加括號的5個位置隨機插空,之後再對應生成右括號。隊友調試該模塊也算是費勁了心思(別問我怎麽知道,我看著一個BUG一個BUG改過來的,最後成功一刻差點跪下來謝天謝地),這個代碼優點也非常的明顯,比我的代碼隨機性更大,之後的初中高中也直接加上去就好。但是建議寫成類,做繼承或者多態,會更顯得牛*。
當然代碼也有缺點的地方,查重部分比較拖沓,查重代碼如下:
infile.open(distAll.data()); str = question2(); notok = 0; p = 0; while(getline(infile,s1)) { mypath[p] = s1; p++; } infile.close(); for(int m = 0 ; m < p ; m++) { infile.open(mypath[m].data()); while(getline(infile,s2)) { string tem = ""; tem = s2.substr(4) ; //去掉題號,留下題目進行比對 if(strcmp(str.c_str() , tem.c_str()) == 0) notok = 1; } infile.close(); } if(notok == 0) { outfile << k + 1 ; if((k + 1) < 10) outfile << " "; outfile << "、" << str << endl; outfile << " " << endl; } else k--; Sleep(1000);
三次出題三次查重,自己都承認自己多寫了200多行。
最後有一個BUG當事人和我全都一臉懵逼,摸不著頭腦,就是產生隨機數時,用的種子,結果每個隨機數產生前不加sleep就不會有變化???請往上看這個代碼,最後一個Sleep(1000),雖然解決了BUG,但是程序生成每個題目要等待1s,太慢了爸爸!真香...
執筆至此,不再多言,隊友已經柳眉倒豎,怒目圓睜看著我,就差擼袖子上了,為了求生欲,下次再聊。
結對編程-人和代碼都長得好看系列