一、课程实习目的
本课程的教学目标是训练学生综合运用程序设计语言进行问题描述和设计完整的程序解决实际应用问题的能力。通过本课程的实习,能够将程序设计中多个知识点有机地串连起来,学会将现实中的具体问题建模,并最终通过程序实现来解决相应的问题。
本课程的基本要求是:实习前做好预习,对实习目的、要求、流程等方面有清楚的了解;要求独立完成实习,实习过程中仅选用最基本的库函数,不得调用未经允许的库函数来求解相应的问题。
二、实验环境
三、实习内容及过程
1、课程实习题目及内容
简易机器翻译器
背景:在一些比较秘密的场景中,为了传输一些重要的数值串(例如人数),相关人员往往将这些数据保存为字符串、数值以及各种符号混用的形式。在实际需要这些数据时,就通过提前定义好的方法将它们翻译回来。
相应的子程序(注:子程序可根据实际情况逐年进行调整)
[1] 求最小公倍数和最大公约数
[2] 已知公元1年1月1日是星期一,计算给定的日期是星期几
[3] 求组合数
[4] 输入某雇员的每周工作时间(以小时计)和每小时的工资数,计算并输出他的工资。(如果时间小于0或大于一周的总时间直接输出0)。若雇员每周工作小时超过60小时,则超过60小时的部分按原工资3倍的加班工资来计算,雇员每周工作小时超过40小时而不超过60小时的工资按原工资的1.5倍的加班工资来计算。
[5] 计算n!
明确需要覆盖的点:
[1] 使用子程序
[2] 面向对象编程
[3] 考虑异常
[4] 程序中全部输出都采用格式化输出
用C++或Python实现以下内容:
(1)从特定文本文件中读取一个特定格式字典(训练输入、循环、文件操作),包括①从数字到数值的对应,②从字母到数值的对应,③括号和计算符号不作转换(训练泛型)。
(2)再从一个文本文件中读取原始符号串,并实现将原始符号串根据字典进行必要的转换。注意,转换后的符号串需要用链表存储。(训练循环、指针、链表操作)
(3)针对转换后的符号串,按下列要求完成相应的计算,并用两侧各附有一个空格的计算结果替换原来的符号串。(训练参数子程序调用)
①对[a, b]形式的要替换为a和b的最小公倍数,对{a, b}形式的要替换为a和b的最大公约数(计算最小公倍数和最大公约数,训练变量交换)
②对(a/b/c)形式的要求解a年b月c日是周几,并用这个数值替换(计算星期数,训练分支、逻辑运算)
③对(a, b)形式,要求解从a个中挑选b个的组合数,并替换(计算组合数,训练迭代和递归)
④对<a,b>形式,要求计算员工周工资,其中a是工作小时数,b是每小时工资数,并替换(计算员工周工资,训练分支)
⑤对(a)形式,要求计算阶乘,并替换(计算阶乘,训练迭代和递归)
(4)再针对处理后的、由空格分隔的数据进行排序(训练排序处理)
(5)最后将排好序的数据,以格式化的形式存入文件。(训练循环、文件操作,格式化输出)
2、程序中使用的主要符号及函数名说明
如:int GCD(int a, int b)// GCD()函数求a,b最大公约数
#include
#include
#include
#include
using namespace std;
//辗转相除法求最大公约数
struct node {
string data;
node* next;
};
int maxdiv(int a, int b) {
if (b == 0)
return a;
return maxdiv(b, a % b);
}
int minmul(int a, int b) {
return a * b / maxdiv(a, b);
}
int calculatorweekday(int y, int m, int d) {
if (m == 1 || m == 2) {
m += 12;
y--;
}
//基姆拉尔森公式
int week = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
return week + 1;
}
//阶乘
int factorial(int n) {
int sum = 1;
if (n == 0) {
return sum;
}
for (int i = 1; i <= n; i++) {
sum *= i;
}
return sum;
}
//组合数
int combination(int n, int m) {
if (n < 1 || n < m) {
cout << "错误运行,错误代码:";
return 0;
}
return factorial(n) / (factorial(m) * factorial(n - m));
}
//求工资
double salary(int weekti, int hoursa) {
if (weekti < 0 || weekti > 168) {
return 0;
}
if (weekti > 60)
return (3 * weekti - 110) * hoursa;
if (weekti > 40 && weekti < 60)
return (1.5 * weekti - 20) * hoursa;
return weekti * hoursa;
}
//创建链表
void readdatafromfile(node*& head,int num) {
node* r, * s;
r = head;
string filename= "c:/users/administration/desktop/程序设计/dict(1).txt";
cout << "请输入字典文件路径: " << endl;
//cin >> filename;
fstream ifs;
ifs.open(filename, ios::in);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
string * dict =new string[2*(num+1)];
int i = 0;
//字典读取
while (ifs >> dict[i]) {
i++;
}
ifs.close();
//输出字典
cout << "*************" << endl;
cout << "*" << " 字典 " << "*" << endl;
for (int g = 0; g < 2 * (num + 1); g+=2) {
cout <<"*" <<setw(3)<<""<< dict[g] << " " << setw(3) << dict[g + 1] <<setw(3)<< "*" << endl;
}
cout << "*************" << endl;
cout << "请输入input文件名" << endl;
string inputname= "c:/users/administration/desktop/程序设计/input.txt";
//cin >> inputname;
ifs.open(inputname, ios::in);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
string buf;
//对应字典
cout << "-------split-------" << endl;
cout << "调试转换后字符串:" << endl;
while (ifs >> buf) {
for (int j = 0; j < buf.size(); j++) {
for (int l = 0; l < 2 * (num + 1); l += 2) {
//一个数字换多个数字
if (buf[j] <= 57 && buf[j] > 48) {
if (int(buf[j]) == int(dict[l][0]) && atoi(buf.c_str())== atoi(dict[l].c_str())) {
int temp = buf.size();
string p = buf.substr(0, j);
string q = buf.substr(j + 1, temp - j - 1);
buf = p + dict[l + 1] + q;
}
}
//字母转换任意位数字
else {
if (int(buf[j]) == int(dict[l][0])) {
int temp = buf.size();
string p = buf.substr(0, j);
string q = buf.substr(j + 1, temp - j - 1);
//buf[j] = dict[l + 1][0];
buf = p + dict[l + 1] + q;
}
}
}
}
s = new node;
s->data = buf;
r->next = s;
r = s;
}
r->next = NULL;
delete[] dict;
ifs.close();
}
bool initlist(node*& l) {
l = new node;
if (l == NULL)
return false;
l->next = NULL;
return true;
}
//第一个中间符号前的数
int transfor1(string a, char c = ',') {
int num1 = 0;
int b = a.find(c);
for (int i = 1; i < b; i++) {
num1 += (a[i] - 48) * pow(10, b - i - 1);
}
return num1;
}
//只有一个中间符号的后一个数
int transfor2(string a, char c = ',') {
int num2 = 0;
int b = a.find(c);
for (int i = b + 1; i < a.size() - 1; i++) {
num2 += (a[i] - 48) * pow(10, a.size() - i - 2);
}
return num2;
}
//有两个中间符号的中间一个数
int transfor3(string a, char c = '/') {
int num3 = 0;
int b = a.find(c);
int d = a.find(c, b + 1);
for (int i = b + 1; i < d; i++) {
num3 += (a[i] - 48) * pow(10, d - i - 1);
}
return num3;
}
//有两个中间符号的最后一个数
int transfor4(string a, char c = '/') {
int num3 = 0;
int b = a.find(c);
int d = a.find(c, b + 1);
for (int i = d + 1; i < a.size() - 1; i++) {
num3 += (a[i] - 48) * pow(10, a.size() - i - 2);
}
return num3;
}
//无中间符号
int transfor5(string a, char c = ')') {
int num1 = 0;
int b = a.find(c, 1);
for (int i = 1; i < b; i++) {
num1 += (a[i] - 48) * pow(10, b - i - 1);
}
return num1;
}
bool douhao(string a) {
for (int i = 0; i < a.size(); i++)
{
if (a[i] == 44)
return true;
}
return false;
}
bool xiegan(string a) {
for (int i = 0; i < a.size(); i++)
{
if (a[i] == 47)
return true;
}
return false;
}
//转义
void translate(node* l) {
while (l) {
//最小公倍数
if (l->data[0] == 91) {
cout << l->data << "的最小公倍数结果为: " << minmul(transfor1(l->data), transfor2(l->data)) << endl;
l->data = to_string(minmul(transfor1(l->data), transfor2(l->data)));
}
//最大公约数
if (l->data[0] == 123) {
cout << l->data << "的最大公约数结果为: " << maxdiv(transfor1(l->data), transfor2(l->data)) << endl;
l->data = to_string(maxdiv(transfor1(l->data), transfor2(l->data)));
}
//组合数
if (l->data[0] == 40 && douhao(l->data)) {
cout << l->data << "C" << transfor1(l->data) << ',' << transfor2(l->data) << "=" << combination(transfor1(l->data), transfor2(l->data))<<endl;
l->data = to_string(combination(transfor1(l->data), transfor2(l->data)));
}
//星期几
if (l->data[0] == 40 && !douhao(l->data) && xiegan(l->data)) {
cout << l->data <<"即"<< transfor1(l->data, '/') << "年" << transfor3(l->data, '/') << "月" << transfor4(l->data, '/') << "日" << "是星期" <<
calculatorweekday(transfor1(l->data, '/'), transfor3(l->data, '/'), transfor4(l->data, '/'))<<endl;
l->data = to_string(calculatorweekday(transfor1(l->data, '/'), transfor3(l->data, '/'), transfor4(l->data, '/')));
}
//阶乘
if (l->data[0] == 40 && !douhao(l->data) && !xiegan(l->data)) {
cout << l->data << "即" << transfor5(l->data) << "的阶乘: " << factorial(transfor5(l->data))<<endl;
l->data = to_string(factorial(transfor5(l->data)));
}
//工资
if (l->data[0] == 60) {
cout << l->data<<"即工资为: " << salary(transfor1(l->data), transfor2(l->data))<<endl;
l->data = to_string(salary(transfor1(l->data), transfor2(l->data)));
}
l = l->next;
}
}
//排序
void msort(node* head) {
if (head == NULL || head->next == NULL)
return;
node* newhead = head;
node* cur = head;
node* tail = NULL;
for (newhead; newhead->next != NULL; newhead = newhead->next) {//单链表冒泡排序的外层循环仅仅其循环计数作用,而数组的外层循环也可以代表要确定位置的元素。
for (cur = head; cur->next != tail; cur = cur->next) {//内层循环永远从头开始遍历到上次的尾部
string t;
if (atoi(cur->data.c_str()) > atoi(cur->next->data.c_str())) {
t = cur->data;
cur->data = cur->next->data;
cur->next->data = t;
}
}
tail = cur;
}
}
//读取字典行数
int read() {
ifstream file;
string filename= "c:/users/administration/desktop/程序设计/dict(1).txt";
cout << "读取字典行数,请输入字典文件路径: " << endl;
//cin >> filename;
file.open(filename, ios::in);
string a[2];
int i = 0;
while (file >> a[i]) {
if (i == 1) { break; }
i++;
}
file.close();
return atoi(a[1].c_str());
}
//写入文件
void write(node * head) {
ofstream ofs;
ofs.open("c:\\users\\administration\\desktop\\程序设计\\output.txt", ios::out);
while (head) {
ofs << head->data<<endl;
head = head->next;
}
ofs.close();
}
//删除链表,避免内存泄漏
void delete_chain(node *& head)
{
node * pnext = head->next;
while (pnext != NULL)
{
delete head;
head = pnext;
pnext = head->next;
}
delete head;
head = NULL;
}
//打印链表里的每一个数据
void PrintList(node *& linklist)
{
if (linklist == NULL || linklist->next == NULL)
{
cout << endl << "链表为空!" << endl;
return;
}
else
{
cout << "链表元素为:";
node* p = linklist;
while (p->next != nullptr)
{
p = p->next;
if (p->next == nullptr)
{
cout << p->data << endl;
break;
}
cout << p->data << "->";
}
}
}
int main() {
int p = read();
node* l;
//创建链表
initlist(l);
//读取以及写入文件
readdatafromfile(l, p);
//转义
translate(l);
cout << "-------split-------" << endl;
cout << "运算后结果:" << endl;
PrintList(l);
cout << "-------split-------" << endl;
cout << "调试排序后结果:" << endl;
//排序
msort(l);
PrintList(l);
write(l);
delete_chain(l);
}
实验结果分析: 简易机器翻译器能够简单得对于给定特殊的字典和需要翻译的内容进行机械式的翻译,采用了链表的技术来储存翻译的结果,并用数组存储字典并以之用来进行简单的判断从而进行翻译,在应对实际各种复杂的情况下稍显不足,仍有改进的空间。
技术点覆盖:最小公倍数和最大公约数
计算星期数,训练分支、逻辑运算
计算组合数,训练迭代和递归
计算员工周工资,训练分支
计算阶乘,训练迭代和递归
输入,文件操作,循环,泛型
循环,指针,链表操作
训练循环、文件操作,格式化输出