這個作業屬於哪個課程 | 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,然后執行文本(注意在執行文本時需把文本與編譯后的程序放在一個文件夾中):
對應文本內容為:
輸出答案正確,添加功能成功。