面向对象程序设计寒假作业2编程题
这个作业属于哪个课程 | 2020年面向对象程序设计 (福州大学 - 数学与计算机科学学院) |
---|---|
这个作业要求在哪里 | 面向对象程序设计寒假作业2 |
这个作业的目标 | 1.继续完成编程题。 |
2.新建一个github仓库,并把作业推送到该仓库。 | |
3.发布博客。 | |
作业正文 | 面向对象程序设计寒假作业2实践题 |
面向对象程序设计寒假作业2编程题 | |
Mac下Shell脚本使用学习笔记(一) | |
Mac下Shell脚本使用学习笔记(二) | |
其他参考文献 | Macbook中使用Vim和GCC编译C程序 |
c语言中所有文件操作函数详解 | |
C语言命令行参数详解 | |
freopen()函数的使用 |
编程题
1.题目要求:
(1)继续完成作业一的编程题。
(2)优化架构,一般要求每个函数长度不超过15行。
(3)优化规范,尤其是命名规范。
(4)制作一个编译脚本,运行该脚本可以编译你的代码,可选的脚本语言,python(2.7),windows批处理,powershell,shell。
(5)进行单元测试,即测试每一个函数,并制作一个测试脚本,运行该脚本可以进行测试,并显示测试结果。
(6)添加以下功能:通过命令行读取一个文件,然后运行这个文件。如我的程序叫lang,lang 1.txt
代表我要运行1.txt这个文本中的代码
2.分解需求:
(1)对代码进行进一步优化:
①首先,我注意到在上次编程题的代码中,在一下这两个函数中,其功能存在重复赘余:
int beginning(int wallet,char d[]){
if(strcmp(d,"零")==0) return 0;
else if(strcmp(d,"一")==0) return 1;
else if(strcmp(d,"二")==0) return 2;
else if(strcmp(d,"三")==0) return 3;
else if(strcmp(d,"四")==0) return 4;
else if(strcmp(d,"五")==0) return 5;
else if(strcmp(d,"六")==0) return 6;
else if(strcmp(d,"七")==0) return 7;
else if(strcmp(d,"八")==0) return 8;
else if(strcmp(d,"九")==0) return 9;
else if(strcmp(d,"十")==0) return 10;
else return -1;
}
int calculate(int wallet,char d[]){
if(strcmp(d,"零")==0) return 0;
else if(strcmp(d,"一")==0) return 1;
else if(strcmp(d,"二")==0) return 2;
else if(strcmp(d,"三")==0) return 3;
else if(strcmp(d,"四")==0) return 4;
else if(strcmp(d,"五")==0) return 5;
else if(strcmp(d,"六")==0) return 6;
else if(strcmp(d,"七")==0) return 7;
else if(strcmp(d,"八")==0) return 8;
else if(strcmp(d,"九")==0) return 9;
else if(strcmp(d,"十")==0) return 10;
else return -1;
}
在作业一中之所以,将其分为两个函数是因为,第一个beginning函数其作用在于为变量“钱包”作初始化的作用,而第二个 calculate函数,是执行钱包总额增减的功能。但是,这两个函数在本质上起的是相同的作用,初始化函数也可以等同于对钱包的总额进行增减,故此我将这两个函数进行合并,并更名为change,其作用为对钱包总额的变化,代码如下:
int change(int wallet,char d[]){
if(strcmp(d,"零")==0) return 0;
else if(strcmp(d,"一")==0) return 1;
else if(strcmp(d,"二")==0) return 2;
else if(strcmp(d,"三")==0) return 3;
else if(strcmp(d,"四")==0) return 4;
else if(strcmp(d,"五")==0) return 5;
else if(strcmp(d,"六")==0) return 6;
else if(strcmp(d,"七")==0) return 7;
else if(strcmp(d,"八")==0) return 8;
else if(strcmp(d,"九")==0) return 9;
else if(strcmp(d,"十")==0) return 10;
else return -1;
}
当然,相应的在主函数中,调用函数部分也会有相应的变化(代码见后)。
②然后,我在想,对于输出钱包总额时,我原先采用了三分支判断结构,是否可以进行修改使代码更简洁呢?
原如下代码:
void num(int i){
if(i==0) printf("零");
else if(i==1) printf("一");
else if(i==2) printf("二");
else if(i==3) printf("三");
else if(i==4) printf("四");
else if(i==5) printf("五");
else if(i==6) printf("六");
else if(i==7) printf("七");
else if(i==8) printf("八");
else if(i==9) printf("九");
else if(i==10) printf("十");
}
void sum(int wallet){
int a,b;
if(wallet<=10){num(wallet);}
else if(wallet<20){
a=wallet%10;
printf("十");
num(a);
}
else{
a=wallet/10;
b=wallet%10;
num(a);
printf("十");
if(b!=0) num(b);
}
}
然后我尝试把后两个分支合并,代码如下:
void num(int i){
if(i==0) printf("零");
else if(i==1) printf("一");
else if(i==2) printf("二");
else if(i==3) printf("三");
else if(i==4) printf("四");
else if(i==5) printf("五");
else if(i==6) printf("六");
else if(i==7) printf("七");
else if(i==8) printf("八");
else if(i==9) printf("九");
else if(i==10) printf("十");
}
void sum(int wallet){
int a,b;
if(wallet<=10){num(wallet);}
else{
a=wallet/10;
b=wallet%10;
if(a!=0)num(a);//注意修改前的这一步
printf("十");
if(b!=0) num(b);
}
}
我按照我修改后的代码尝试了三种情况,如下:
前两种按照中文习惯是不存在任何问题的,但是第三种出现六问题,因为按照中文习惯,当钱包总额为17时,应该输出十七,而不是一十七。注意到,我在上一段代码作出的标记:
if(a!=0)num(a);
所以我改变了,这个判断语句a的条件,目的是为了当a=1时,其没有输出,故代码为:
void num(int i){
if(i==0) printf("零");
else if(i==1) printf("一");
else if(i==2) printf("二");
else if(i==3) printf("三");
else if(i==4) printf("四");
else if(i==5) printf("五");
else if(i==6) printf("六");
else if(i==7) printf("七");
else if(i==8) printf("八");
else if(i==9) printf("九");
else if(i==10) printf("十");
}
void sum(int wallet){
int a,b;
if(wallet<=10){num(wallet);}
else{
a=wallet/10;
b=wallet%10;
if(a!=1)num(a);//注意修改后的这一步
printf("十");
if(b!=0) num(b);
}
}
验证结果(如下),发现答案正确:
③然后,我开始解决主函数的一些判断问题:
原代码如下:
int main() {
char a[100],b[100],c[100],d[100];
int wallet=0,i,flag=0;
scanf("%s %s %s %s",a,b,c,d);
if(strcmp(a,"整数")!=0||strcmp(c,"等于")!=0) flag=1;
for(i=1;flag==0;i++){
if(i==1) wallet=change(wallet,d);
else {
scanf("%s",a);
if(strcmp(a,b)!=0&&strcmp(a,"看看")!=0) break;
scanf("%s",c);
if(strcmp(a,"看看")==0){
sum(wallet);
break;}
scanf("%s",d);
if(strcmp(c,"增加")==0) wallet+=change(wallet,d);
if(strcmp(c,"减少")==0) wallet-=change(wallet,d);
}
}
return 0;
}
当初,由于时间关系,我忽略了一个问题就是,如果我在第一次输入变量名为钱包后,在进行钱包总额的增减时,如果我输入的是其他变量名,那其总额仍会进行变化吗,于是我进行了测试:发现程序会结束,原来之前已经考虑过了这种情况,好吧,有点白费功夫了
④最后,优化规范,我对一些不规范对命名的调整,总代码如下:
#include <stdio.h>
#include <string.h>
int change(int wallet,char d[]){
if(strcmp(d,"零")==0) return 0;
else if(strcmp(d,"一")==0) return 1;
else if(strcmp(d,"二")==0) return 2;
else if(strcmp(d,"三")==0) return 3;
else if(strcmp(d,"四")==0) return 4;
else if(strcmp(d,"五")==0) return 5;
else if(strcmp(d,"六")==0) return 6;
else if(strcmp(d,"七")==0) return 7;
else if(strcmp(d,"八")==0) return 8;
else if(strcmp(d,"九")==0) return 9;
else if(strcmp(d,"十")==0) return 10;
else return -1;
}
void num(int i){
if(i==0) printf("零");
else if(i==1) printf("一");
else if(i==2) printf("二");
else if(i==3) printf("三");
else if(i==4) printf("四");
else if(i==5) printf("五");
else if(i==6) printf("六");
else if(i==7) printf("七");
else if(i==8) printf("八");
else if(i==9) printf("九");
else if(i==10) printf("十");
}
void printf_sum(int wallet){
int a,b;
if(wallet<=10) num(wallet);
else{
a=wallet/10;
b=wallet%10;
if(a!=1) num(a);
printf("十");
if(b!=0) num(b);
}
}
int main() {
char a[100],b[100],c[100],d[100];
int wallet=0,i,flag=0;
scanf("%s %s %s %s",a,b,c,d);
if(strcmp(a,"整数")!=0||strcmp(c,"等于")!=0) flag=1;
for(i=1;flag==0;i++){
if(i==1) wallet=change(wallet,d);
else {
scanf("%s",a);
if(strcmp(a,b)!=0&&strcmp(a,"看看")!=0) break;
scanf("%s",c);
if(strcmp(a,"看看")==0){
printf_sum(wallet);//这里重新命名函数,使代码更加易读
break;}
scanf("%s",d);
if(strcmp(c,"增加")==0) wallet+=change(wallet,d);
if(strcmp(c,"减少")==0) wallet-=change(wallet,d);
}
}
return 0;
}
待解诀问题:仍然只能进行零至十的赋值,将在下一阶段进行思考和改进。
由于个人的力量是有限的,所以希望大家,可以对我的代码提出进一步对改进建议,或者你的看法。
(2)制作一个编译脚本,运行该脚本编译代码(学习笔记已在链接标出):
shell代码如下:
#!/bin/bash
echo -n "请输入需要编译的文件名:"
read compile_file
gcc $compile_file -o test1.out
./test1.out
结果如下:
之后,为了使一运行脚本便可编译代码,作出了修改,代码如下:
#!/bin/bash
gcc main.c -o test1.out
./test1.out
结果如下:
然后,我又参考了沈欢同学的作业,进行进一步完善了脚本,增加了一个判断,用于辨别是否成功编译。代码如下:
#!/bin/bash
gcc main.c -o test1.out
if [ $? == 0 ] # $?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
then
echo "编译成功"
./test1.out
if [ $? == 0 ]
then
echo "运行结束"
else
echo "运行失败"
fi
else
echo "编译失败"
fi
结果如下:
需要注意的是,当我们在使用脚本编译代码的时候,需要将代码文件与脚本放在一个文件夹中,否则会出现以下情况:
(3)进行单元测试,并制作一个测试脚本,运行并显示结果:
这个地方,理解了半天,后面参考了提交的同学的博客和询问了同学,我才明白什么意思(非常感谢)
注意到我的代码中总共有三个函数,而后面两个num函数与printf_sum函数皆为输出函数,负责直接输出结果,因此这两个函数的测试我们可以在结果中看到,因此这里只需测试chang函数:
//main.h
#include <stdio.h>
#include <string.h>
int change(char d[]){
if(strcmp(d,"零")==0) return 0;
else if(strcmp(d,"一")==0) return 1;
else if(strcmp(d,"二")==0) return 2;
else if(strcmp(d,"三")==0) return 3;
else if(strcmp(d,"四")==0) return 4;
else if(strcmp(d,"五")==0) return 5;
else if(strcmp(d,"六")==0) return 6;
else if(strcmp(d,"七")==0) return 7;
else if(strcmp(d,"八")==0) return 8;
else if(strcmp(d,"九")==0) return 9;
else if(strcmp(d,"十")==0) return 10;
else return -1;
}
测试部分代码:
#include "main.h"
void judge_change(int number_actual,int number_example){
if(number_actual==number_example) printf("Pass\n");
else printf("Fail\n");
}
void test_change(){
printf("test_change begins\n");
judge_change(change("零"),0);
judge_change(change("一"),1);
judge_change(change("二"),2);
judge_change(change("三"),3);
judge_change(change("四"),4);
judge_change(change("五"),5);
judge_change(change("六"),6);
judge_change(change("七"),7);
judge_change(change("八"),8);
judge_change(change("九"),9);
judge_change(change("十"),10);
judge_change(change("二十"),-1);
printf("test_change is over\n");
}
int main() {
test_change();
return 0;
}
按照(2)的步骤,我们建立相应的脚本文件,脚本代码如下:
#!/bin/bash
gcc main.c -o test2.out
if [ $? == 0 ]
then
echo "编译成功"
./test2.out
if [ $? == 0 ]
then
echo "运行结束"
else
echo "运行失败"
fi
else
echo "编译失败"
fi
打开终端进行测试,结果如下:
Change 函数测试过后,我们来通过结果查看其他两个函数的正误:
以上就是测试部分的内容,感谢沈欢同学的帮助。
(4)添加功能:过命令行读取一个文件,然后运行这个文件
一看到题目疑惑不解,当学完shell后还以为就是Shell 文件包含重定向,后面发现并不是,这样,查了资料(链接已贴出)发现原来在我们c语言课本最后一章中有提到过对文件对操作(当初老师没教,而且考试也不考,就没看细看这一章,可是这次放假书放学校了,等回学校的时候再好好看看书)。以下是我的代码:
//main.h
#include <stdio.h>
#include <string.h>
int change(int wallet,char d[]){
if(strcmp(d,"零")==0) return 0;
else if(strcmp(d,"一")==0) return 1;
else if(strcmp(d,"二")==0) return 2;
else if(strcmp(d,"三")==0) return 3;
else if(strcmp(d,"四")==0) return 4;
else if(strcmp(d,"五")==0) return 5;
else if(strcmp(d,"六")==0) return 6;
else if(strcmp(d,"七")==0) return 7;
else if(strcmp(d,"八")==0) return 8;
else if(strcmp(d,"九")==0) return 9;
else if(strcmp(d,"十")==0) return 10;
else return -1;
}
void num(int i){
if(i==0) printf("零");
else if(i==1) printf("一");
else if(i==2) printf("二");
else if(i==3) printf("三");
else if(i==4) printf("四");
else if(i==5) printf("五");
else if(i==6) printf("六");
else if(i==7) printf("七");
else if(i==8) printf("八");
else if(i==9) printf("九");
else if(i==10) printf("十");
}
void printf_sum(int wallet){
int a,b;
if(wallet<=10){num(wallet);}
else{
a=wallet/10;
b=wallet%10;
if(a!=1)num(a);
printf("十");
if(b!=0) num(b);
}
}
void test(){ //注意把原代码的main函数改名,便于后续引用。
char a[100],b[100],c[100],d[100];
int wallet=0,i,flag=0;
scanf("%s %s %s %s",a,b,c,d);
if(strcmp(a,"整数")!=0||strcmp(c,"等于")!=0) flag=1;
for(i=1;flag==0;i++){
if(i==1) wallet=change(wallet,d);
else {
scanf("%s",a);
if(strcmp(a,b)!=0&&strcmp(a,"看看")!=0) break;
scanf("%s",c);
if(strcmp(a,"看看")==0){
printf_sum(wallet);
break;}
scanf("%s",d);
if(strcmp(c,"增加")==0) wallet+=change(wallet,d);
if(strcmp(c,"减少")==0) wallet-=change(wallet,d);
}
}
printf("\n");
}
//main.c
#include "main.h"
//argc:命令行传入参数的总个数
//argv:*argv[]是一个指针数组,里面存放的指针指向所有的命令行参数,argv[0]指向程序的全局路径,argv[1]指向在DOS命令行中执行程序名后的第一个字符串,argv[2]指向第二个。
int main(int argc,char * argv[]) {
if(argc>1){
//当argc = 1的时候,表示只有一个程序名称;当argc>1时,说明文本的内容已经输入其中。
freopen(argv[1],"r",stdin);
//FILE *freopen(文件名,文件打开的模式,一个文件,通常使用标准流文件);
//"r"代表"只读"
//准流文件具体是指stdin、stdout和stderr。其中stdin是标准输入流,默认为键盘;stdout是标准输出流,默认为屏幕;stderr是标准错误流,一般把屏幕设为默认。
test();
}
//把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区
return 0;
}
然后通过终端将其编译为test,然后执行文本(注意在执行文本时需把文本与编译后的程序放在一个文件夹中):
对应文本内容为:
输出答案正确,添加功能成功。