github地址:https://github.com/includebug/ExerciseGenerate
结对伙伴:王煜墉(3118004972) + 徐伟浩(3118004980)
实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。
自然数:0, 1, 2, …。
其中e, e1和e2为表达式,n为自然数或真分数。
Myapp.exe -n 10
将生成10个题目。
使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如
Myapp.exe -r 10
将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
四则运算题目1
四则运算题目2
……
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
答案1
答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
Myapp.exe -e
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 1260 | 1400 |
Development | 开发 | 900 | 1030 |
· Analysis | · 需求分析 (包括学习新技术) | 30 | 60 |
· Design Spec | · 生成设计文档 | 30 | 20 |
· Design Review | · 设计复审 (和同事审核设计文档) | 20 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
· Design | · 具体设计 | 30 | 30 |
· Coding | · 具体编码 | 50 | 50 |
· Code Review | · 代码复审 | 30 | 25 |
· Test | · 测试(自我测试,修改代码,提交修改) | 30 | 25 |
Reporting | 报告 | 60 | 60 |
· Test Report | · 测试报告 | 20 | 20 |
· Size Measurement | · 计算工作量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 1280 | 1420 |
根据用户输入的不同参数,调用不同功能的模块。
代码如下:
#include <iostream>
#include <fstream>
#include "Fraction.h"
#include "ExerciseGenerate.h"
#include "calculator.h"
using namespace std;
int main(int argc,char* argv[])
{
wcout.imbue(locale("chs"));
if (argc < 3) //参数没有 4个
{
cout << "输入错误参数少于3个,请到cmd下运行" << endl;
system("pause");
return 1;
}
if (strcmp(argv[1], "-n") == 0 && strcmp(argv[3], "-r") == 0)
{
wofstream ofs1, ofs2;
ofs1.open("Exercise.txt", ios::app);
ofs2.open("Result.txt", ios::app);
ofs1.imbue(locale("chs"));
ofs2.imbue(locale("chs"));
int ExerciseNumber, MaxNum;
ExerciseNumber = atoi(argv[2]);
MaxNum = atoi(argv[4]);
ExerciseGenerate *x = new ExerciseGenerate[ExerciseNumber];
int j = 0;
for (int i = 0; i < 20 && j < ExerciseNumber; i++)
{
x[j].ExerciseGenerateMaxNum(MaxNum);
x[j].generateExpress();
Expression temp = x[j].returnExpression();
Fraction z = temp.returnResult();
if (z.isPositive())
{
wstring y = temp.returnExpressionWstring();
ofs1 << j + 1 << L"、 ";
ofs1 << y << endl;
y = z.FractionWstring();
ofs2 << j + 1 << L"、 ";
ofs2 << y << endl;
j++; //读取下一个表达式
i = 0; //重新计数,超过20次不能存进去则跳出
}
}
delete[]x;
ofs1.close();
ofs2.close();
}
else if (!strcmp(argv[1], "-e") && !strcmp(argv[3], "-a"))
{
string ExerciseFile = argv[2];
string AnswerFile = argv[4];
ReadANSI(ExerciseFile, AnswerFile);
}
system("pause");
return 0;
}
计算器是整个程序中难度最大的一部分,是后面判断表达式的值与用户所填写的值是否相同的关键。实现计算器的算法有很多,如栈,递归,或者是利用后序遍历二叉树的方法。这里我们采用最为暴力的一种解法,这就利用if else进行判断求值。流程图如下:
代码如下:
#include "calculator.h"
#include "Fraction.h"
int simplify(int a, int b) //求最大公约数
{
int c = 0;
while (c = a % b) //c==0时 循环结束
{
a = b;
b = c;
}
return b;
}
bool isNo(wchar_t c) //is Number
{
return c >= L‘0‘ && c <= L‘9‘ || c == L‘\‘‘ || c == L‘/‘;
}
bool isSyb(wstring c)//is Symbol
{
return (c[0] == L‘+‘ || c[0] == L‘-‘ || c[0] == L‘ב || c[0] == L‘÷‘) && c.size() == 1; //ver1.2
}
bool isSyb(wchar_t c)//is Symbol
{
return c == L‘+‘ || c == L‘-‘ || c == L‘ב || c == L‘÷‘;
}
bool isIlg(wchar_t c) //is illegal
{
return !isNo(c) && !isSyb(c) && !(c == L‘(‘ || c == L‘)‘ || c == L‘ ‘);
}
wstring GetSubtext(wstring S, size_t bgn, size_t end) //follow "left-inclusive interval"
{
wstring subtext(end - bgn, L‘ ‘);
for (size_t i = bgn; i != end; i++)
subtext[i - bgn] = S[i];
return subtext;
}
vector<wstring> split(const std::wstring &s, wchar_t delim) //切割wstring,以delim符号为分界
{
if (!s.find(delim))
{
cout << "没有这个字符,该数不是分数" << endl;
}
vector<wstring> elems;
wstringstream ss;
ss.str(s);
std::wstring item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
bool spiltNum(int a[3], wstring x) //把分数的wstring传进去,返回三个整数,存在数组a里
{
vector<wstring> k, k1;
k = split(x, L‘\‘‘);
if (k.at(0) == x) //判断是否切割成功
return false;
k1 = split(k.at(1), L‘/‘);
if (k1.at(0) == k.at(1))
return false;
a[0] = stoi(k.at(0));
a[1] = stoi(k1.at(0));
a[2] = stoi(k1.at(1));
return true;
}
wstring calculatorFractionAndInt(wstring x, wstring y, char z)
{
int b = stoi(y);
int a[3];
spiltNum(a, x);
int c[3];
wstring TempResult;
if (z == ‘+‘)
{
c[0] = a[0] + b;
c[1] = a[1];
c[2] = a[2];
}
else if (z == ‘-‘)
{
c[0] = a[0] - b;
c[1] = a[1];
c[2] = a[2];
}
else if (z == ‘*‘)
{
c[0] = a[0] * b;
c[1] = a[1] * b;
c[2] = a[2];
}
else
{
c[0] = 0;
c[1] = a[0] * a[2] + a[1];
c[2] = a[2] * b;
}
if (c[2] != 0 && c[1] != 0)
{
int d = simplify(c[1], c[2]);
c[1] /= d;
c[2] /= d;
}
if (c[1] > c[2] && c[2] != 0)
{
c[0] += c[1] / c[2];
c[1] = c[1] % c[2];
}
TempResult = to_wstring(c[0]) + L‘\‘‘ + to_wstring(c[1]) + L‘/‘ + to_wstring(c[2]);
return TempResult;
}
wstring calculatorIntAndFraction(wstring x, wstring y, char z)
{
int a = stoi(x);
int b[3];
spiltNum(b, y);
int c[3];
wstring TempResult;
if (z == ‘+‘)
{
c[0] = a + b[0];
c[1] = b[1];
c[2] = b[2];
}
else if (z == ‘-‘)
{
c[0] = a - b[0] - 1;
c[1] = b[2] - b[1];
c[2] = b[2];
}
else if (z == ‘*‘)
{
c[0] = b[0] * a;
c[1] = b[1] * a;
c[2] = b[2];
}
else
{
c[0] = 0;
c[1] = b[0] * b[2] + b[1];
c[2] = b[2] * a;
}
if (c[2] != 0 && c[1] != 0)
{
int d = simplify(c[1], c[2]);
c[1] /= d;
c[2] /= d;
}
if (c[1] > c[2] && c[2] != 0)
{
c[0] += c[1] / c[2];
c[1] = c[1] % c[2];
}
TempResult = to_wstring(c[0]) + L‘\‘‘ + to_wstring(c[1]) + L‘/‘ + to_wstring(c[2]);
return TempResult;
}
wstring calculatorFraction(wstring x, wstring y, char z)
{
int a[3], b[3];
spiltNum(a, x);
spiltNum(b, y);
int c[3];
wstring TempResult;
if (z == ‘+‘)
{
c[0] = a[0] + b[0];
c[1] = a[1] * b[2] + a[2] * b[1];
c[2] = a[2] * b[2];
}
else if (z == ‘-‘)
{
c[0] = a[0] - b[0];
c[1] = a[1] * b[2] - a[2] * b[1];
c[2] = a[2] * b[2];
if (c[1] < 0)
{
c[0]--;
c[1] = c[2] - c[1];
}
}
else if (z == ‘*‘)
{
c[0] = 0;
c[1] = (a[0] * a[2] + a[1])*(b[0] * b[2] + b[1]);
c[2] = a[2] * b[2];
}
else
{
c[0] = 0;
c[1] = (a[0] * a[2] + a[1])*b[2];
c[2] = a[2] * (b[0] * b[2] + b[1]);
}
if (c[2] != 0 && c[1] != 0)
{
int d = simplify(c[1], c[2]);
c[1] /= d;
c[2] /= d;
}
if (c[1] > c[2] && c[2] != 0)
{
c[0] += c[1] / c[2];
c[1] = c[1] % c[2];
}
TempResult = to_wstring(c[0]) + L‘\‘‘ + to_wstring(c[1]) + L‘/‘ + to_wstring(c[2]);
return TempResult;
}
wstring calculatorInt(wstring x, wstring y, char z)
{
int a[3];
wstring CtmpResult;
int tempResult;
if (z == ‘+‘)
{
tempResult = stoi(x) + stoi(y);
}
if (z == ‘-‘)
{
tempResult = stoi(x) - stoi(y);
}
if (z == ‘*‘)
{
tempResult = stoi(x) * stoi(y);
}
if (z == ‘/‘)
{
if (stoi(y) == 0)
{
CtmpResult = L"0\‘0/0";
return CtmpResult;
}
else if (stoi(x) % stoi(y) == 0)
tempResult = stoi(x) / stoi(y);
else
{
int num1 = stoi(x), num2 = stoi(y);
a[0] = num1 / num2;
a[1] = num1 % num2;
a[2] = num2;
CtmpResult = to_wstring(a[0]) + L‘\‘‘ + to_wstring(a[1]) + L‘/‘ + to_wstring(a[2]);
return CtmpResult;
}
}
CtmpResult = to_wstring(tempResult); // int to wstring
return CtmpResult;
}
wstring BasicCalculation(wstring x, wstring y, char z)
{
wstring CtmpResult;
if (string::npos != x.find(L"\‘") && string::npos != y.find(L"\‘")) //两个分数计算
CtmpResult = calculatorFraction(x, y, z);
else if (string::npos == x.find(L"\‘") && string::npos != y.find(L"\‘")) //左边整数,右边分数
CtmpResult = calculatorIntAndFraction(x, y, z);
else if (string::npos != x.find(L"\‘") && string::npos == y.find(L"\‘")) //左边分数,右边整数
CtmpResult = calculatorFractionAndInt(x, y, z);
else //两个整数
CtmpResult = calculatorInt(x, y, z); // int to wstring
return CtmpResult;
}
wstring calc(wstring S, size_t len)
{
deque<wstring> words;
bool inNo = false; //door for isNo
//bool inSyb = false; //door for isSyb
size_t NoPos = 0;
//size_t SybPos = 0;
size_t ParPos = 0, ParDep = 0;
size_t begin = 0, end = len;
for (size_t temp = 0; temp < len; temp++)
{
if (S[temp] == L‘、‘)
begin = temp + 1;
if (S[temp] == L‘=‘)
end = temp;
}
for (size_t i = begin; i <= end; i++) //build standard formula
{
if (isIlg(S[i]) && i != end) //formula illegal checking
return L"The formula is illegal1!";
if (isNo(S[i]) && !inNo) //Number postion begin
{
inNo = true;
NoPos = i;
}
if (!isNo(S[i]) && inNo) //Number postion end
{
inNo = false;
words.push_back(GetSubtext(S, NoPos, i));
}
if (isSyb(S[i])/* && !inSyb*/)
{
words.push_back(GetSubtext(S, i, i + 1));
}
if (S[i] == L‘)‘)
return L"RightParentheses is not match!";
if (S[i] == L‘(‘)
{
//inPar = true;
ParDep++;
ParPos = i + 1; //left-inclusive interval
size_t j;
for (j = i + 1; ParDep && j <= end; j++)
{
if (S[j] == L‘(‘) ParDep++;
if (S[j] == L‘)‘) ParDep--;
}
if (j <= end)
{
wstring recResult(calc(GetSubtext(S, ParPos, j - 1), GetSubtext(S, ParPos, j - 1).size()));
if (recResult == L"The formula is illegal!")
return L"The formula is illegal2!";
else if (recResult == L"Parentheses is not match!")
return L"LeftParentheses is not match!";
else
words.push_back(recResult);
}
else
{
//parentheses is not match
return L"LeftParentheses is not match!";
}
i = j - 1; //jump over subtext
}
} //end for
int tempResult = 0;
wstring CtmpResult;
//check formula
if (words.size() == 0) return L"0"; // 0 element
if (words.size() == 1) //1 element
if (!isSyb(words.front()))
{
//strcpy_s(CtmpResult, words.front().c_str());
CtmpResult = words.front();
return CtmpResult;
}
else
{
wstring s(L"The formula is illegal3!");
return s;
}
if (words.front() == L"×" || words.front() == L"÷") //front is symble
return L"The formula is illegal4!";
if (words.front() == L"+" || words.front() == L"-")
{
if (!isSyb(*(words.begin() + 1)))
{
words.front() += *(words.begin() + 1);
words.erase(words.begin() + 1); //erase second one
}
else
return L"The formula is illegal5!";
}
if (isSyb(words.back()))
{
wcout.imbue(locale("chs"));
wcout << words.back() << endl;
return L"The formula is illegal6!";
}
for (deque<wstring>::iterator i = words.begin() + 1; i < words.end(); i++)
{
if ((*i == L"×" || *i == L"÷") && (isSyb(*(i - 1))) && (*(i - 1)).size())
return L"The formula is illegal7!";
if ((*i == L"+" || *i == L"-") && (isSyb(*(i - 1))) && (*(i - 1)).size())
if (!isSyb(*(i + 1)))
{
*(i + 1) = *i + *(i + 1);
i = words.erase(i);
}
else
return L"The formula is illegal8!";
}
//start calculate
for (deque<wstring>::iterator i = words.begin(); i != words.end(); i++)
{
//calculate * and /
if (*i == L"×")
{
CtmpResult = BasicCalculation(*(i - 1), *(i + 1), ‘*‘);
i = words.erase(i - 1);
i = words.erase(i);
*i = CtmpResult;
}
if (*i == L"÷")
{
CtmpResult = BasicCalculation(*(i - 1), *(i + 1), ‘/‘);
i = words.erase(i - 1); //erase函数返回下一个的对象,相当于数组的i++
i = words.erase(i);
*i = CtmpResult;
}
}
for (deque<wstring>::iterator i = words.begin(); i != words.end(); i++)
{
//calculate + and -
if (*i == L"+")
{
CtmpResult = BasicCalculation(*(i - 1), *(i + 1), ‘+‘);
i = words.erase(i - 1);
i = words.erase(i);
*i = CtmpResult;
}
if (*i == L"-")
{
CtmpResult = BasicCalculation(*(i - 1), *(i + 1), ‘-‘);
i = words.erase(i - 1);
i = words.erase(i);
*i = CtmpResult;
}
}
CtmpResult = words.front();
return CtmpResult;
}
void ReadANSI(string expressionFilename, string resultFilename) //ANSI编码的txt读取
{
wstring b;
wstring c;
wifstream infile;
wifstream infile1;
infile.imbue(locale("chs"));
infile1.imbue(locale("chs"));
wcout.imbue(locale("chs"));
//ANSI型保存的txt才能用
infile.open(expressionFilename);
infile1.open(resultFilename);
if (!infile.is_open())
{
exit(EXIT_FAILURE);
}
int i = 0;
while (!infile.eof())
{
i++;
getline(infile, b);
getline(infile1, c);
if (b.size() == 0 && c.size() == 0)
return;
wstring result1 = calc(b, b.size());
if (string::npos == result1.find(L"\‘"))
result1 = result1 + L"\‘0/1";
Fraction temp(result1);
wcout << temp.FractionWstring() << endl;
if (string::npos == c.find(temp.FractionWstring()))
cout << "error:" << i << endl;
else
cout << "right:" << i << endl;
wcout << b << endl;
}
infile.close();
}
void ReadUTF8(string expressionFilename, string resultFilename) //不带BOM的utf8的读取
{
wstring_convert<codecvt_utf8<wchar_t> > conv;
ifstream ifs(expressionFilename);
ifstream ifs1(resultFilename);
if (!ifs.is_open())
{
exit(EXIT_FAILURE);
}
int i = 0;
while (!ifs.eof())
{
i++;
string line1;
getline(ifs, line1);
wstring wb1 = conv.from_bytes(line1);
string line2;
getline(ifs, line2);
wstring wb2 = conv.from_bytes(line2);
wcout << calc(wb1, wb1.size()) << endl;
if (string::npos == wb2.find(calc(wb1, wb1.size())))
cout << "error:" << i << endl;
else
cout << "right:" << i << endl;
wcout << wb1 << endl;
}
ifs.close();
}
利用c语言的rand函数生成随机数量的n个操作数以及n-1个操作符生成不同的表达式。代码如下:
#include "ExerciseGenerate.h"
bool ExerciseGenerate::ExerciseGenerateMaxNum(int i)
{
numSize = i;
return true;
}
wstring ExerciseGenerate::generateExpress()
{
srand(clock());
int i = rand() % 3 + 2;
for (int k = 0; k < i; k++) //3*3的二维数组存数据,一行为一个数据
{
int isFraction = rand() % 2; //是否为整数,若为整数a[k][1]=0,即分子置为0
if (isFraction == 0)
{
for (int j = 0; j < 3; j++)
{
if (j == 2) //保证分母不为0
a[k][j] = (rand() % (10 - 1)) + 1;
else
a[k][j] = rand() % 10;
}
}
else
{
for (int j = 0; j < 3 ; j++)
{
if (j == 2)
a[k][j] = (rand() % (10 - 1)) + 1;
else
a[k][j] = rand() % 10;
}
a[k][1] = 0;
}
}
wchar_t oprtr[3];
for (int p = 0; p < i-1; p++)
{
int u = rand() % 4;
if (u == 0)
oprtr[p] = L‘+‘;
else if(u==1)
oprtr[p] = L‘-‘;
else if (u == 2)
oprtr[p] = L‘ב;
else
oprtr[p] = L‘÷‘;
}
int bracketsNum[2];
if (i == 2)
{
bracketsNum[0] = 0;
bracketsNum[1] = 0;
}
else if (i == 3)
{
bracketsNum[0] = rand() % 2;
if (bracketsNum[0] == 0)
bracketsNum[1] = 0;
else
bracketsNum[1] = rand() % 2;
}
else
{
bracketsNum[0] = rand() % 3;
if (bracketsNum[0] == 0)
bracketsNum[1] = 0;
else if(bracketsNum[0] == 1)
bracketsNum[1] = rand() % 5;
else
bracketsNum[1] = rand() % 4;
}
cout << bracketsNum[0] << bracketsNum[1] << endl;
for (int x = 0; x < i; x++)
{
for (int y = 0; y < 3; y++)
{
cout << a[x][y];
}
cout << endl;
}
Fraction fraction[4];
for (int z = 0; z < i; z++)
{
wstring ex = to_wstring(a[z][0]) + L"\‘" + to_wstring(a[z][1]) + L"/" + to_wstring(a[z][2]);
Fraction k(ex);
fraction[z] = k;
fraction[z].print();
}
Expression t(fraction, oprtr, i, bracketsNum);
cout << "i的值" << i << endl;
t.toWstring();
t.CalculateFraction();
t.print();
NewExpression = t;
return t.returnExpressionWstring();
}
Expression ExerciseGenerate::returnExpression()
{
return NewExpression;
}
第一次听说这种一人写代码一人复审的编程方式,听老师介绍的时候感觉很有趣。第一次利用live share进行结对编程,一开始真的很不习惯,甚至出现各种翻车,各种掉线
事情进展得并没有那么顺利,总有不少的磕磕碰碰,但好在最后也完成了项目。伟浩的代码能力很强,实现了很多复杂的功能,错误也很少。
一个自动生成小学四则运算题目的命令行程序(C++)(王煜墉+徐伟浩)
原文:https://www.cnblogs.com/handsomeyong/p/12700938.html