----by core 第四组 ( 邹卫其 范力 )

一. 项目介绍

1. 能自动生成小学四则运算题目并给出答案,生成题目时可以选择下列参数:

   1)生成题目数量

   2)每道题目中运算数数量

   3)运算数数值范围

   4)运算符种类(  +  -  *  /  ^  )

   5)运算数类型(整数,小数,分数)

   6)运算数精度(保留到小数点后几位)

2. 将四则运算的计算功能包装在一个模块中( class 或 DLL)

3. 将 core 模块通过一定的 API 接口 (Application Programming Interface) 来和 UI 组对接

4. UI 组测试所有 core 组 DLL 后,core 组需根据 UI 组的测试结果进行反馈优化

5. 项目管理和撰写博客

6. 项目 Github 地址:https://github.com/eudaem/homework2/pull/2

二. 功能分析

1. 创建 class Node 类, 储存数据信息,所有操作数值以分数存储;以 Node 类为节点建立二叉树存储表达式所有信息。

2. 算术表达式树的树枝天然是运算符,树叶是操作数;先随机生成运算符树,再将树叶补全即可。

3. 在用户选择乘方时我们将 '^' 运算符沉到运算符树底,保证算术表达式不会过于复杂。

4. 对 Node 类重载加、减、乘、除、乘方运算,重载中所有运算均以分数运算完成。

5. 递归计算表达式树的值,递归检测到负数时交换左右子树,保证运算过程不出现负数。

6. 递归生成中缀表达式,并合理地加上括号,以 string 类存储。

7. 封装大类 Core,创建对象 cal,通过成员函数设置相关参数 , 生成的题目和答案以 string 串的形式传递。

 三. 代码实现

1. 定义 Core 类(封装成大类)

 class Core
{
private:
int que_num = ; //题目数量
int data_num = ; //操作数数量
int range = ; //计算的数值范围
int opr_type = ; //操作符种类 0.加减 1.加减乘 2.加减乘除 3.加减乘除乘方
int data_type = ; //0整数 1小数 2分数
int accuracy = ;
string* que = new string[que_num + ];
string* ans = new string[que_num + ];
public:
void set_que_num(int a) { que_num = a; }
void set_data_num(int a) { data_num = a; }
void set_range(int a) { range = a; }
void set_opr_type(int a) { opr_type = a; }
void set_data_type(int a) { data_type = a; }
void set_accuracy(int a) { accuracy = a; }
int get_que_num() { return que_num; }
int get_data_num() { return data_num; }
int get_range() { return range; }
int get_opr_type() { return opr_type; }
int get_data_type() { return data_type; }
int get_accuracy() { return accuracy; }
string *getQue();
string *getAns();
private:
string expstring(Node *root);
Node *creatOptree();
Node calc(Node *root);
void previsite(Node *head);
char operat();
char int_operat();
int get_factor(int a);
};

2. 设置类的参数并调用示例

 int main()
{
Core cal;
string *q, *a; cal.set_data_num();
cal.set_que_num();
cal.set_range();
cal.set_opr_type();
cal.set_data_type();
cal.set_accuracy(); q = cal.getQue();
a = cal.getAns();
for (int i = ; i <= cal.get_que_num(); i++)
{
cout << q[i] << " = " << a[i] << endl << endl;
} system("pause");
return ;
}

3. 定义节点 Node 类

 class Node
{
public:
Node() { top = -; bottom = -; operat = ; left = NULL; right = NULL; }
Node(int t, int b, char opr) { top = t; bottom = b; operat = opr; left = NULL; right = NULL; }
void simp();
int gcd(int p, int q);
Node operator + (Node n2);
Node operator - (Node n2);
Node operator * (Node n2);
Node operator / (Node n2);
Node operator ^ (Node n2);
Node operator = (Node n2); public:
int top;
int bottom;
char operat;
class Node *left;
class Node *right; }; Node Node::operator + (Node n2)
{
Node n;
n.top = top*n2.bottom + bottom*n2.top;
n.bottom = bottom*n2.bottom;
n.operat = ;
n.left = NULL;
n.right = NULL;
n.simp();
return n;
}
Node Node::operator - (Node n2)
{
Node n;
n.top = top*n2.bottom - bottom*n2.top;
n.bottom = bottom*n2.bottom;
n.operat = ;
n.left = NULL;
n.right = NULL;
n.simp();
return n;
}
Node Node::operator * (Node n2)
{
Node n;
n.top = top*n2.top;
n.bottom = bottom*n2.bottom;
n.operat = ;
n.left = NULL;
n.right = NULL;
n.simp();
return n;
}
Node Node::operator / (Node n2)
{
Node n;
n.top = top*n2.bottom;
n.bottom = bottom*n2.top;
n.operat = ;
n.left = NULL;
n.right = NULL;
n.simp();
return n;
}
Node Node::operator ^ (Node n2)
{
Node n;
n.top = (int)pow(top, n2.top);
n.bottom = (int)pow(bottom, n2.top);
n.operat = ;
n.left = NULL;
n.right = NULL;
n.simp(); return n;
}
Node Node::operator = (Node n2)
{
top = n2.top;
bottom = n2.bottom;
operat = n2.operat;
return Node(n2.top, n2.bottom, );
}

4. 随机生成一颗算数表达式树

 Node* Core::creatOptree()
{
Node *head = NULL, *p = NULL, *q = NULL;
int i = , j = ;
head = new Node;
head->operat = '+'; for (i = , j = ; i < data_num - ; i++)
{
p = head;
while (p)
{
if (rand() % ) {
if (p->left) p = p->left;
else {
q = new Node;
if (data_type == ) q->operat = int_operat();
else q->operat = operat();
p->left = q;
break;
}
}
else {
if (p->right) p = p->right;
else {
q = new Node;
if (data_type == ) q->operat = int_operat();
else q->operat = operat();
p->right = q;
break;
}
}
}//end while
}//end if
return head;
} void Core::previsite(Node *head)
{
if (!head) return;
class Node *q = NULL, *p = NULL;
if (head->operat && opr_type == && !head->left && !head->right)
{
int temp = ;
temp = rand() % ;
if (temp == ) head->operat = '^';
} if (head->operat && data_type == && (opr_type == || opr_type == ) && !head->left && !head->right)
{
int temp = ;
temp = rand() % ;
if (temp == ) head->operat = '/';
} if (head->operat && !head->left)
{
p = new Node;
//reset(q);
p->bottom = rand() % range + ;
p->top = rand() % range + ;
if (data_type == ) p->bottom = ; //整数
if (data_type == && head->operat == '/') p->top = rand() % range + ;
if (data_type == ) //小数
{
p->bottom = ;
p->top = rand() % ( * range);
}
if (head->operat == '^')
{
int min = (range < ) ? range : ;
p->bottom = ;
p->top = rand() % min + ;
}
head->left = p;
}
if (head->operat && !head->right)
{
q = new Node;
//reset(q);
q->bottom = rand() % range + ;
q->top = rand() % range + ;
if (data_type == ) q->bottom = ; //整数
if (data_type == && head->operat == '/') q->top = get_factor(head->left->top);
if (data_type == ) //小数
{
q->bottom = ;
q->top = rand() % ( * range);
}
if (head->operat == '^') //乘方
{
int min = (range <= ) ? range : ;
q->bottom = ;
q->top = rand() % min + ;
}
head->right = q;
}
previsite(head->left);
previsite(head->right); }

5. 计算表达式树数值 

 Node Core:: calc(Node *root)   //计算树的值
{
Node answer;
if (root == NULL)
return answer;
char opr = root->operat; if (opr == )
{
return *root;
}
else if (opr == '+')
{
return calc(root->left) + calc(root->right);
}
else if (opr == '-')
{
answer = (calc(root->left) - calc(root->right));
if (answer.top < )
{
Node *temp = root->left;
root->left = root->right;
root->right = temp;
answer.top *= -;
}
return answer;
}
else if (opr == '*')
{
return calc(root->left) * calc(root->right);
} else if (opr == '/')
{
if (data_type == )
{ }
return calc(root->left) / calc(root->right);
} else if (opr == '^')
{
return calc(root->left) ^ calc(root->right);
}
else
return answer;
}

6. 打印中缀表达式

 string Core::expstring(Node *root)  //生成中缀表达式  分子分母间用 |
{
if (root == NULL)
return que[];
if (root->left)
{
if ((root->left->operat == '+' || root->left->operat == '-') && (root->operat == '*' || root->operat == '/'))
{
que[] += "( ";
expstring(root->left);
que[] += ") ";
}
else if (root->operat == '^' && root->left->operat)
{
que[] += "( ";
expstring(root->left);
que[] += ") ";
}
else
expstring(root->left);
} if (root->operat == )
{
if (data_type == )
{
double t = 1.0 * root->top / root->bottom;
stringstream s1;
s1 << t;
que[] += s1.str();
que[] += " ";
}
else
{
stringstream s2;
s2 << root->top;
que[] += s2.str();
if (root->bottom > )
{
que[] += "|";
stringstream s3;
s3 << root->bottom;
que[] += s3.str();
}
que[] += " ";
}
}
else
{
que[] += root->operat;
que[] += " ";
} if (root->right)
{
if ((root->operat == '-' || root->operat == '*') && (root->right->operat == '-' || root->right->operat == '+'))
{
que[] += "( ";
expstring(root->right);
que[] += ") ";
}
else if (root->operat == '/' && (root->right->operat && root->right->operat != '^'))
{
que[] += "( ";
expstring(root->right);
que[] += ") ";
}
else if (root->operat == '^'&& root->right->operat)
{
que[] += "( ";
expstring(root->right);
que[] += ") ";
}
else
expstring(root->right);
}
return que[];
}

7. 生成题目 string 数组

 string* Core::getQue()
{
Node *head, result;
que = new string[que_num + ];
ans = new string[que_num + ];
for (int i = ; i <= que_num; i++)
{
head = creatOptree();
previsite(head);
result = calc(head);
result.simp();
if (result.bottom == || result.top < )
i--;
else
{
que[] = "";
que[i] = expstring(head);
if (data_type == )
{
double t = 1.0 * result.top / result.bottom;
stringstream s1;
s1 << t;
ans[i] += s1.str();
}
else
{
stringstream s2;
s2 << result.top;
ans[i] += s2.str();
if (result.bottom > )
{
ans[i] += "|";
stringstream s3;
s3 << result.bottom;
ans[i] += s3.str();
}
}
}
}
return que;
}

8. 生成答案 string 数组

 string* Core::getAns()
{
if (data_type == )
{
for (int i = ; i <= que_num; i++)
{
for (int j = ; ans[i][j]; j++)
if (ans[i][j] == '.')
{
for (size_t k = j + accuracy + ; k < ans[i].length(); k++)
ans[i][k] = '\0';
break;
}
}
}
return ans;
}

四. 测试

以下测试样例约定生成 20 道题目,每题 10 个运算数,数值范围 0~10,在加、减、乘、除、乘方模式。

1. 整数

 (  -  /  ) * (  +  ^  ) +  ^  - (  +  )
=
/ + - + * - ( + - * )
=
( - + ) * ( - ( ^ - ) ) + + +
=
* ( * - ) - / + + ^ +
=
* ^ + * + * ( + ( - ) * )
=
* ( * - ) * + ( * + - ) *
=
( + ) * ( - ) + ( / + - ) * *
=
( + * ) * + ^ * ( / + / )
=
- / - ( - ) + ^ * ( - / )
=
* - - / + / * * ^
=
/ + + / + * + - ( + )
=
* * ( / + ) + - ( - ) + -
=
* ( - + ) + ^ + + * -
=
+ - - + ( + ) * ( ^ + / )
=
( + - / ) * * + * * ( - )
=
* ( - ) - ( - / ) + * * *
=
+ + / * / + * - /
=
( ^ + / ) * ( + * ) + * ( + )
=
( + ^ ) * ^ * + + + /
=
* / + ( + + ) * ( / - / )
=

View string

2. 小数(精度为 3)

 ( 1.05 -  ^  ) / ( 1.75 * ( 6.1 - 3.2 ) ) + 3.4 + 3.1 + 6.15 + 8.9
= 21.559
( 6.85 + 4.1 ) * 7.2 + 1.65 / ( ^ * 2.9 + 1.45 + 5.6 + 8.05 )
= 78.840
( 9.55 / ( 7.95 - 7.4 ) - 0.65 ) / 7.3 + ( 0.1 + 1.8 ) / ( 9.8 - 3.4 / 6.7 )
= 2.494
1.2 / ( 6.15 + 5.15 - 9.75 - 0.45 ) + 2.9 / ( 9.9 / 9.9 ) * 1.1 / 1.9
= 2.769
( 3.8 - 1.05 ) * 3.75 / ^ + ( 9.25 + 4.4 ) * 7.85 * ^
=
( 4.1 / ( 5.05 * 9.3 ) + 9.25 - 0.4 ) * ^ + 1.1 - ( 2.5 - 2.2 )
= 36.549
3.7 + ^ + 9.6 * + ( 8.6 - 7.15 - 0.1 ) * ( 1.6 + 6.25 )
= 1348.7
4.7 + 8.55 + 3.2 - 2.55 + ( 4.35 - 0.5 ) * ( 5.75 + ( 4.1 + 4.35 ) / 2.15 )
= 51.168
( ^ - 7.65 ) * ^ + 7.85 / 5.4 / ( 0.05 * 9.65 * )
= 390.03
7.95 - 6.45 + ^ + 8.15 * 5.55 + ( 3.75 + 7.1 ) / ( 8.75 - 1.05 )
= 49.141
^ - 0.55 / 3.9 + 6.6 / ^ * ( + 6.75 ) / 7.25
= 93.376
( ^ - 0.05 ) / ( 3.15 - 0.2 ) + 8.55 - 8.2 + 1.6 * 8.75 + 5.65
= 25.406
3.6 / ( ( 8.45 - 1.15 ) / 8.5 + 1.4 * ( 7.15 - 3.15 ) ) + ( 1.05 + 4.4 ) * 9.4
= 51.787
( 1.25 + 7.45 ) / ( ^ / ^ ) + ^ * ^
= 15628.9
^ - ( 4.45 + 2.65 + 2.15 ) + 0.1 * ( 8.45 - ( 9.6 - 7.25 ) * 0.4 )
= 3116.5
^ + 5.5 - ( 4.1 - 1.95 ) + ( 7.05 - 4.45 ) * 9.4 - ( 1.4 - )
= 28.39
^ - 8.2 - ( 3.3 - 0.8 ) + 6.55 * 5.9 * 7.05 / ( 4.05 - 1.5 )
= 160.142
3.3 + 9.8 / 7.9 + 2.9 * 4.9 + 9.6 - ( 3.9 + 2.4 ) * 8.75 / 6.8
= 20.243
4.45 - ( 9.25 + 0.75 - ( 4.65 + ) ) + ( 8.95 + 9.1 ) * 1.3 - 8.05 / 7.9
= 26.546
8.1 / 0.75 / ^ + ( 8.35 - 6.6 ) * ^ / ^
= 4.72

View string

3. 分数

 ( | - | / | ) / ( | * ( | - | ) ) + | + | + | + |
= |
| * | / | + | * | + / ( ^ + | + | )
= |
- | - ( | - | ) * | + ( | - | ) * | * ( | - | )
= |
* ( | - | ) + ( | - | ) * | - ( | + | * | ) * |
= |
| / * ( | - | ) / | / | + | - | / ( | - | )
= |
( | - | ) / ( | * ) + | + | + ( | - | ) * ( | - | )
= |
( | + | - ( | - | ) ) * ( | + | + | ) + | / | + |
= |
| + ^ + | * | + | + | + | + | - |
= |
| + - | - | - ( | - | ) + | * | * | /
= |
^ * | * | + ^ / ( ( | + | ) / ( * | ) )
= |
( - | ) / ( * | + | ) + ^ - | * ^
= |
| - | - ( | - | ) + ( | - | ) / ( ( | + | ) * ( | - | ) )
= |
| * ( - | ) / ( | * | ) * | * | + | * ( - | )
= |
| + | + | - | + ^ + ( | - | * | ) * |
= |
^ + | + ^ * | + | + | * | / |
= |
^ + | - ( | - | ) + ( - | ) / | * ( + | )
= |
| * ( | + | ) * ( | / | - | ) + | / | * | / |
= |
| / | * | * ^ + * ( / | + | - | )
= |
| + + | - | / ( | * | + ) + | / * |
= |
/ ( | - | ) / ( | + | ) + | + | + | - ( | + | )
= |

View string

4. 样例分析

可以看到,我们生成的算数表达式符合参数要求,结构均匀而随机,括号匹配无误,计算结果正确。

五. DLL 封装和对接

参考网上知识,采用 VS2017 创建 DLL 文件,在 .h 头文件中进行类的定义和相关声明,在 .cpp 文件中进行各种成员函数的定义。在完成 DLL 相关配置后,生成新的解决方案,生成相应的 .lib 和 .dll 文件。

编写测试程序,测试 DLL 文件,将 .h .lib 和 .dll 文件复制到相应文件夹,在头文件中引用 .h 文件,在资源文件引用 .lib 文件,在 main 函数中创建对象,引用成员函数,检测成功后给 UI 组初步对接。

与 UI 组进行 DLL 对接时,其中一组曾编译出现 “LNK2019 无法解析的外部符号” 的报错,后来排查得知是 x64 和 x86 平台差异引起,于是我们对 x86 和 x64 分别生成 .lib 和 .dll 文件,解决了这个问题。

 

六. 结对编程感悟

这次结对的过程中 zou 主要完成算术表达式的计算,中缀显示,类的封装,DLL 文件的生成;fan 主要完成算数表达式在各种情况下的生成,API 和博客的撰写。

这次编程非常佛系最后一天上午才完成,然后忙着和 UI 组对接,总的来说结对编程的效率远高于独立作业,我们将思路理顺之后编程非常高效,出现 bug 之后也能及时发现并解决。

面对再一次的编程任务,我们抱以一种佛系的心态,一开始并未着急编写,吸取上次经验,讨论了很多种方案,仔细分析优劣,最终理清思路并梳理了整体的框架,有条不紊地编写时并未出现太多意料之外的困难。

面对问题的处理方式是多种多样的,结对编程优势之一就是,两个人的积极性互相被调动,个人陷入局部困境导致整体目标变模糊的概率也大为减小,与之同时,不论焦急还是从容,死限 DDL 来临的速率是一个常数,所以不妨用更加佛系的心态,减少实际行动中漫无目的或毫无效率忙碌,更多地在思维活跃而清晰之下充分调用自身能力,稳健地去面对。

七. PSP 表格

Personal Software Process Stages 预估耗时(分) 实际耗时(分)
计划 10 10
· 估计这个任务需要多少时间 10 10
开发 1495 1540
· 需求分析 (包括学习新技术) 2h * 60 3h * 60
· 生成设计文档 30 20
· 设计复审 (和同事审核设计文档) 5 5
· 代码规范 (为目前的开发制定合适的规范) 10 5
· 具体设计 30 30
· 具体编码 12h * 60 10h * 60
· 代码复审 30 40
· 测试(自我测试,修改代码,提交修改) 10h * 60 11h * 60
报告 70 60
· 测试报告 30 30
· 计算工作量 10 10
· 事后总结, 并提出过程改进计划 30 20
  1555 1620

  

最新文章

  1. Mac下打开eclipse 始终提示 你需要安装Java SE 6 Runtime
  2. 代码中特殊的注释技术——TODO、FIXME和XXX的用处
  3. Android之startActivityForResult的使用
  4. ural 1273. Tie
  5. cpu,内存,虚拟内存,硬盘,缓存之间是什么关系??
  6. Object-C: 枚举
  7. sping获取bean方法 解决资源耗尽
  8. Introduction to the POM
  9. Jobbox.io(职位盒子): 新兴的面向技术人才的职场招聘众推平台
  10. firefox同步数据时无响应问题
  11. Oracle客户端的安装
  12. 生活常用类API调用的代码示例合集:邮编查询、今日热门新闻查询、区号查询等
  13. Docker最全教程——Redis容器化以及排行榜实战(十三)
  14. pdf文件下载水印添加的中文与空格问题解决
  15. [转]AJAX POST请求中参数以form data和request payload形式在servlet中的获取方式
  16. mongdb的索引及备份
  17. Oracle创建&#39;数据库&#39;三步走
  18. Spring boot中普通工具类不能使用@Value注入yml文件中的自定义参数的问题
  19. vue实现pc端无限加载功能
  20. Java学习路线教程之JDBC基本操作

热门文章

  1. shell (3) 磁盘挂载
  2. Mastache.js学习笔记(转自小花喵)
  3. 如何使用新的glibc来编译自己的程序
  4. cv2.FileNode has no keys
  5. Go语言包管理工具Glide
  6. Oracle PL/SQL编程之过程
  7. android studio Cannot resolve symbol &#39;@drawable/XXX&#39;等问题解决办法
  8. html5的meta标签
  9. php.ini配置max_execution_time和FPM配置request_terminate_timeout
  10. How to push master to QA branch in GIT