homework-03 图形化化最大子序列和
你现在使用的代码规范是什么, 和上课前有什么改进?
我们一开始使用的是C++完成的相关程序。本次因为一些原因,改为C#进行编写。因为2013-10-21在VS2012中,所以所有的代码都已经被VS自动格式化,代码编写风格很“规范”。对于代码的设计规范,我们分为了两个部分:主程序,计算模块。主程序负责图形化显示,和用户交互。负责监测是否已经存在已经运行的进程,并负责程序的更新。计算模块负责计算被选择出来的Matrix块。在我们结对编程的时候,为了使得两个部分分开,计算模块被设计成为了一个单独的类,在出程序构造该类之后,就可以直接获得计算得到的模块。计算模块所有的信息,由构造时主线程传入的string类型CMD决定。 曾经我们的代码设计都是面向过程的编写方式,不具有可移植性。本次实现的各个部分,之间耦合度很低。可以方便的移植。
在代码风格选择上,我在缩进,括号,换行都遵循了VS规范的风格,在命名上对于一般变量采用“驼峰命名”规则,在模块命名上采用首字母大写的驼峰,常量命名则采用了全大写的方式,函数命名上则同样采用了首字母大写的驼峰规则。
你的同伴有哪些优点 (列出至少三点), 和那些需要改进的地方 (列出至少三点)。
优点 | 1、代码基础好。 2、易于交流。 3、参加过结对编程 |
缺点 | 1、一开始沟通没做好。 2、时间表安排有问题。 3、效率略低 |
你的代码从 作业2 到 作业3 经历了哪些变化? 哪些代码需要重构 (看关于代码重构的资料), 哪些需要重写, 为什么?
我负责的部分是主程序的编写,包括了UI设计,进程的单态维护,线程调度等模块。而在作业2中的代码主题不需要变动,但函数接口需要重新封装,加入返回文件名,数组被选中的位置等相关参数,在C#的编码中,我们将以前的过程式程序重新封装了一个类来更好的完成任务,但是由于以前我使用C++编码的,所以在代码迁移的过程中我们选择了重新编写代码的方式,只复制了核心代码。
原先的C++代码接口(C语言风格)
MAXSUM_API int maxsum(int mat[][MAXCOL], int row_size, int col_size, int rRow, int rCol); MAXSUM_API int maxUnicomBlock(int mat[][MAXCOL], int row_size, int col_size, int rRow, int rCol); void VDoubleExtend(int mat[][MAXCOL], int row_size, int col_size) ; void HDoubleExtend(int mat[][MAXCOL], int row_size, int col_size) ;
C++运算逻辑实现
const int inf = 0x7fffffff; int N, M; bool HEXT(0), VEXT(0), FROMFILE(0), FINDRECT(1); int maxsum(int* c, int size) { int ret = -inf, minpre = 0, prefix = 0; for(int i = 0; i < size; i++) { prefix += c[i]; if(prefix - minpre > ret) ret = prefix - minpre; if(prefix < minpre) minpre = prefix; } return ret; } int maxsum(int mat[][MAXCOL], int row_size, int col_size, int rRow, int rCol) { int arr[MAXCOL]; int ret = -inf; for(int ni = 0; ni < row_size - rRow + 1; ++ni) for(int nj = 0; nj < col_size - rCol + 1; ++nj) { memset(arr, 0, sizeof(arr)); for(int i = ni; i < ni + rRow && i < row_size; ++i) { for(int j = nj; j < nj + rCol && j < col_size; ++j) arr[j - nj] += mat[i][j]; int tmp = maxsum(arr, rCol); getmax(ret, tmp); } } return ret; } int maxsum(int mat[][MAXCOL], int row_size, int col_size) { int arr[MAXCOL]; int ret = -inf; for(int i = 0; i < row_size; ++i) { memset(arr, 0, sizeof(arr)); for(int j = i; j < row_size; ++j) { for(int k = 0; k < col_size; ++k) arr[k] += mat[j][k]; int tmp = maxsum(arr, col_size); getmax(ret, tmp); } } return ret; } int** apply2DIntArr(int row, int col) { int **ret = new int*[row]; for(int i = 0; i < row; ++i) ret[i] = new int[col]; return ret; } void release2DIntArr(int** arr, int row, int col) { for(int i = 0; i < row; ++i) delete[col] arr[i]; delete[row] arr; } void colorBlock(int** mat, int row_size, int col_size, int row, int col, int color) { queue<pair<int, int> > Q; Q.push(make_pair(row, col)); while(!Q.empty()) { pair<int, int> cur = Q.front(); Q.pop(); int r = cur.first, c = cur.second; if(r < 0 || r >= row_size || c < 0 || c >= col_size) continue; if(mat[r][c] == -1) { mat[r][c] = color; Q.push(make_pair(r - 1, c)); Q.push(make_pair(r, c - 1)); Q.push(make_pair(r + 1, c)); Q.push(make_pair(r, c + 1)); } } } int _maxUnicomBlock(int** mat, int row_size, int col_size) { int** map = apply2DIntArr(row_size, col_size); //for(int i = 0; i < row_size; ++i) memset(map[i], 0, sizeof(int) * col_size); for(int i = 0; i < row_size; ++i) for(int j = 0; j < col_size; ++j) map[i][j] = (mat[i][j] >= 0) ? -1 : 0; //color positive block int block_num = 0; for(int i = 0; i < row_size; ++i) for(int j = 0; j < col_size; ++j) if(map[i][j] == -1) colorBlock(map, row_size, col_size, i, j, ++block_num); //count block value int* block = new int[block_num + 1]; memset(block, 0, sizeof(block) * (block_num + 1)); for(int i = 0; i < row_size; ++i) for(int j = 0; j < col_size; ++j) block[map[i][j]] += mat[i][j]; int ret = -inf; for(int i = 1; i <= block_num; ++i) getmax(ret, block[i]); release2DIntArr(map, row_size, col_size); return ret; } int maxUnicomBlock(int mat[][MAXCOL], int row_size, int col_size, int rRow, int rCol) { int** rect; int ret = -inf; //apply for memory rect = apply2DIntArr(rRow, rCol); //main program for(int ni = 0; ni < row_size - rRow + 1; ++ni) for(int nj = 0; nj < col_size - rCol + 1; ++nj) { for(int i = ni; i < ni + rRow; ++i) for(int j = nj; j < nj + rCol; ++j) rect[i - ni][j - nj] = mat[i][j]; int tmp = _maxUnicomBlock(rect, rRow, rCol); getmax(ret, tmp); } //release memory release2DIntArr(rect, rRow, rCol); return ret; } void VDoubleExtend(int mat[][MAXCOL], int row_size, int col_size) { for(int i = 0; i < row_size; ++i) for(int j = 0; j < col_size; ++j) mat[i][j + col_size] = mat[i][j]; } void HDoubleExtend(int mat[][MAXCOL], int row_size, int col_size) { for(int i = 0; i < row_size; ++i) for(int j = 0; j < col_size; ++j) mat[i + row_size][j] = mat[i][j]; }
C++调用方式
int main(int argc, char** args) { char filename[256]; for(int i = 1; i < argc; ++i) { if(strcmp(args[i], "/h") == 0) HEXT = true; if(strcmp(args[i], "/v") == 0) VEXT = true; if(strcmp(args[i], "/a") == 0) FINDRECT = false; if(args[i][0] != '/') FROMFILE = true, memcpy(filename, args[i], strlen(args[i])+1); } if(FROMFILE) inputFromFile(filename); int scanRow(N), scanCol(M); if(VEXT) VDoubleExtend(c, N, M), M *= 2; if(HEXT) HDoubleExtend(c, N, M), N *= 2; int ans; if(FINDRECT) ans = maxsum(c, N, M, scanRow, scanCol); else ans = maxUnicomBlock(c, N, M, scanRow, scanCol); printf("%d\n", ans); system("pause"); return 0; }
C#封装的类
public class ProcessCore { public int[,] table; public bool[,] select; string[] mode; int[,] labx, laby, hrlabx, hrlaby; int pointerx, pointery, targetx, targety; int n, m; bool isHorizontal; public int cnt; public ProcessCore(string cmd) { string[] cmdsplit = cmd.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); //cnt = cmdsplit.Length; //return; string filename = cmdsplit[1]; mode = new string[cmdsplit.Length - 2]; for (int i = 0; i != mode.Length; ++i) mode[i] = cmdsplit[i + 2]; string[] strs = File.ReadAllLines(@filename); if (strs.Length < 2) Console.WriteLine("文件格式不合法"); string[] subStrs = strs[0].Split(','); isHorizontal = false; n = int.Parse(subStrs[0]); subStrs = strs[1].Split(','); m = int.Parse(subStrs[0]); table = new int[n, m]; select = new bool[n, m]; for (int i = 2; i != 2 + n; ++i) { subStrs = strs[i].Split(','); for (int j = 0; j != m; ++j) table[i - 2, j] = int.Parse(subStrs[j]); } int tmp; if (n > m) { for (int i = 0; i != n; ++i) for (int j = 0; j != n; ++j) { tmp = table[i, j]; table[i, j] = table[j, i]; table[j, i] = tmp; } tmp = n; n = m; m = tmp; isHorizontal = true; } this.Calcute(); } private void Regular() { Solve(n, n, m); GetArea(); } private void Ring() { Solve(n, n * 2, m); GetArea(); } private void Horizontal() { Solve(n, n, m); GetArea(); } private void Vertical() { Solve(n, n * 2, m); GetArea(); } private void InRegular() { Regular(); GetArea(); } private void GetArea() { if (pointerx > 0) { for (int i = targetx; i <= pointerx; ++i) for (int j = targety; j <= pointery; ++j) select[i % n, j] = true; } if (pointerx < 0) { for (int i = targetx; i <= (-pointerx); ++i) { for (int j = 0; j != targety; ++j) select[i % n, j] = true; for (int j = -pointery + 1; j != m; ++j) select[i % n, j] = true; } } } private void Solve(int n, int lmt, int m) { long[,] f = new long[n, m]; labx = new int[n, m]; laby = new int[n, m]; hrlabx = new int[n, m]; hrlaby = new int[n, m]; const long NINF = -9999999999; const long INF = 9999999999; long ans = NINF, tmp = 0, seq = 0, hrAns = INF, hrTot = 0, hrMax = NINF, hrSeq = 0;//hr used for horizontal mode for (int i = 0; i != table.GetLength(0); ++i) for (int j = 0; j != table.GetLength(1); ++j) f[i, j] = table[i, j]; if (lmt != table.GetLength(0)) for (int i = 0; i != table.GetLength(0); ++i) for (int j = 0; j != table.GetLength(1); ++j) f[i + table.GetLength(0), j] = table[i, j]; for (int i = 1; i != f.GetLength(0); ++i) for (int j = 0; j != m; ++j) f[i, j] += f[i - 1, j]; pointerx = 0; pointery = 0; int hrpointerx = 0, hrpointery = 0; int hrtargetx = 0, hrtargety = 0; for (int i = -1; i != n; ++i) for (int j = i + 1; j != lmt && j - i <= n; ++j) { seq = 0; for (int k = 0; k != m; ++k) { if (i == -1) tmp = f[j, k]; else tmp = f[j, k] - f[i, k]; if (seq > 0) labx[j, k] = labx[j, k - 1]; else labx[j, k] = i + 1; if (seq > 0) laby[j, k] = laby[j, k - 1]; else laby[j, k] = k; if (seq > 0) seq = seq + tmp; else seq = tmp; if (ans < seq) { ans = seq; pointerx = j; pointery = k; targetx = labx[j, k]; targety = laby[j, k]; } if (isHorizontal) { hrTot += tmp; hrMax = Math.Max(hrMax, tmp); if (hrSeq < 0) hrlabx[j, k] = hrlabx[j, k - 1]; else hrlabx[j, k] = i + 1; if (hrSeq < 0) hrlaby[j, k] = hrlaby[j, k - 1]; else hrlaby[j, k] = k; if (hrSeq < 0) hrSeq = hrSeq + tmp; else hrSeq = tmp; if (hrAns > hrSeq) { hrAns = hrSeq; hrpointerx = j; hrpointery = k; hrtargetx = hrlabx[j, k]; hrtargety = hrlaby[j, k]; } } } if (isHorizontal) { if (ans < 0 && hrTot == hrAns && hrMax < 0) ;//on purpose else { if (ans < hrTot - hrAns) { ans = hrTot - hrAns; pointerx = -hrpointerx; pointery = -hrpointery; targetx = hrtargetx; targety = hrtargety; } } hrAns = INF; hrMax = NINF; hrTot = 0; hrSeq = 0; } } } public bool Calcute() { switch (mode.Length) { case 0: Regular(); break; case 1: switch (mode[0][1]) { case 'a': InRegular(); break; case 'h': isHorizontal = !isHorizontal; if (isHorizontal) Horizontal(); else Vertical(); break; case 'v': if (isHorizontal) Horizontal(); else Vertical(); break; default: return false; } break; case 2: Ring(); break; case 3: InRegular(); break; default: return false; } return true; } }
你的设计是如何保证 不同的 maxsum.exe 命令行最后在一个GUI 的界面显示的? (C++ 的设计模式中有 singleton 的概念, 说明一个类的实例如何在一个进程中保持单例, 我们这里谈的是软件如何在操作系统中保持 singleton)
首先需要有一个方式来实现矩阵的显示。为此我是用在一个Form中绘制一个叫做DataGridView的东西。因为这个是本人第一个C#程序,所以所有代码都是边查边写。所有的参考资料来源于MSDN。每个格子里面的数字来自于计算模块里面的一个数组。然后又另一个等大的矩阵来记录每一个格子是否被选中。通过DataGridView类,实现了基本的可视化。
为了保证只有一个程序显示(多次打开的,显示在已运行的程序之内),我采用了单例模式的设计方式。首先通过命名管道,尝试连接0.5秒,如果已经有程序在运行,那么会连接上,然后将信息传递过去。当前进程终止。否则建立一个新的server进程。当进程建立,或者来了一个新的请求的时候,借助事件与委托模型,将命令参数传递给运算模块。等运算模块构造完成的时候,就已经可以得到数据内容以及被选择的block。然后再次借助事件与委托模式,建立一个新的tab,将内容显示出来。在主窗口关闭后,再次使用命名管道来发送关闭信息,关闭所有线程。
代码复审
在原来的设计中,在进程单态中我采用了的时新启动进程后新用运算单元计算数据,然后将结果通过管道传输给主进程,在复审中我注意到在这种模式下, 将会长时间存在两个同名进程,非常不舒服,而且要运算结构包含两个大矩阵,通讯负担很重,发现这一点后我采取了一种新方式,我注意到如果将命令参数直接传给主进程的话主进程由于当前目录可能不同,所以主进程可能不能正确访问的指定的文件,因此我将当前路径同时传过去,在设置为当前路径就可以再主进程里进行运算了,这样改进后原有的维护单进程的周期就大大减少,通讯的压力也小了很多。
时间记录
预计时间 | 15h | 实际时间 | 17.5h |
代码规范 | 1h | 1h | |
具体设计 | 1h | 2h | |
具体编码 | 10h | 13h | |
代码复审 | 1h | 0.5h | |
代码测试 | 1h | 0.5h | |
测试报告 | 1h | 0.5h |