自獨善上次作業后,余繼而觀此作業,無力不從心之感,雖有坎坷,究竟有方可循,有法可依,並不似上次那般,毫無頭緒,亂來一氣,此乃進步也,心中百般驚喜,自不再話下。幾經修改,終是成功,雖難登大雅之堂,亦有可圈可點之處。欲知代碼為何,輕觸此處。
以余之愚見,此次作業,重點如下:知如何調用,明有無參數,能及時報錯,懂數符轉換,辨負號減號,曉孰先孰后,會處理括號。其中,又以后三者為難點。具體為何,且聽余細細道來。
一、知如何調用
調用之法甚易,先添int argc, char* argv[]
於int main()
括號中,即為int main(int argc, char* argv[])
。此題要求用命令行進行運行,故而不用輸入語句。而后按window+r
,則現一搜索窗口,輸cmd
並按確認,便可見一黑底白字窗口,輸入.exe
文件之路徑,並輸表達式,按回車即可得結果。其中,argc
是命令行總的參數個數,每輸入一個參數,它會自動加一。argv[]
則是argc
個參數的數組,其中第0個參數是目標程序的全名,即路徑加可執行文件名,以后的參數命令行后面跟的是用戶輸入的參數。換言之,在main
函數里的cin>>string
被string=argv[]
替代,即string
由qrgv[]
傳入值,而不是cin
。比如,此題中,應先輸入路徑(假設為e\calculator.exe
),再輸入表達式(假設為6+3-7*8
),即,argv[0]="e\calculator.exe"
,argv[1]="6+3-7*8"
,那么我們需要的就是argv[1]
.此外,須知沒有cin
語句,無法用編譯器直接運行,只能通過此法。
二、明有無參數
依題之要求,如見參數"-a",需先輸出原表達式,再輸出結果。余之做法如下:判斷輸入的參數個數,如果是3個,說明包含"-a",並做一標記。最后,如有標記,則先輸原表達式,加"="再輸結果。
if (argc == 3)
{
k = 1; //標記是否有參數-a
string = argv[2]; //由命令行傳入string的值
}
if (argc == 2)
string=argv[1];
......
if (k == 1) //有參數“-a”,輸出原表達式
print.printstring(q);
cout << output << endl; //輸出結果
示例:
三、能及時報錯
報錯應分為三類:其一,計算過程中數字長度超過10位;其二,除數為零;其三,括號不匹配。具體如下:
其一
int length_check(double number) //檢查計算過程中數字是不是超出范圍
{
int n;
string string;
stringstream stream;
stream.clear(); //
stream << number; //數字轉字符串
stream >> string; //
n = string.size();
if (n > 10)
return 0;
else
return 1;
}
......
if (length_check(t) == 0) //數字長度的判斷
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
其二
if (g == "/"&&f == 0) //g為運算符號,f為除數
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
其三
掃描整個表達式,遇"(",則將其壓入棧,遇")",則刪棧頂元素,最后如棧為空,則正確匹配。
int parentheses_check(string str) //判斷括號是否匹配
{
stack<string>s;
int i, length;
length = str.size();
for (i = 0; i < length; i++)
{
if (str[i] == '(')
s.push("(");
if (str[i] == ')')
{
if (!s.empty() && s.top() == "(")
s.pop();
else
{
return 0;
break;
}
}
}
if (s.empty())
return 1;
else
return 0;
}
......
if (parentheses_check(string) == 0)
cout << "ERROR:parentheses are not matched!" << endl;
}
四、懂數符轉換
數字轉與字符串之間互相轉換,可添加#include<sstream>
用stringstream
實現。注意,如多次轉換,需對stream
進行清除。
如對double a
和string b
數字轉字符串
#include<sstream>
......
stream.clear();
stream << a;
stream >> b;
字符串轉數字
#include<sstream>
......
stream.clear();
stream << b;
stream >> a;
五、辨負號減號
顯然,在一正確表達式中,“-”為負號只有兩種情況,一是位於首位,二是處於“(”之后。在此題中,掃描時直接把負號歸為數字的一部分,一同存進隊列。而減號則作為運算符直接入隊,這樣就把負號和減號分開了。
for (i = 0; i < input.size(); i++)
{
if ((input[i] >= '0' && input[i] <= '9') //對數字和“-”的處理
|| input[i] == '.' || input[i] == '-')
{
if (input[i] == '-')
{
if ((i - 1 >= 0 && input[i - 1] == '(') //負號的判斷
|| (i == 0))
temp = "-";
else
{
queue.push(temp);
temp.clear();
queue.push("-");
}
}
else
temp += input[i];
}
else
{
if (!temp.empty())
queue.push(temp);
temp.clear();
temp = input[i];
if (!temp.empty())
queue.push(temp);
temp.clear();
}
}
if (!temp.empty())
queue.push(temp);
return queue;
}
六、曉孰先孰后
計算,先后順序尤為重要,故必先設法定順序,方可正確計算。此處暫且不論括號,余先以數字規定運算符之大小,
int priority(string c) //判斷優先級
{ //此處只對針加、減、乘、除
if (c == "+" || c == "-") //四種運算,括號下面另外處理
return 0;
if (c == "*" || c == "/")
return 1;
}
並借用2棧之進出,進行計算。二棧為何?一曰數棧,專存數字,一曰符棧,用以放運算符。訪隊列首元並刪之,如為運算符,壓入符棧,如為數字字符串,轉為數字(上以具述)后,壓入數棧。壓入符棧前,需先比其與棧頂元素之大小。唯符棧為空或其值大於棧頂元素,方可將其壓入;如若不然,取數棧頂兩元素,先行計算,得其值而壓入數棧。計算以一函數為之,
double calculate1(double a, string c, double b) //用於計算的函數
{
if (c == "+")
return (a + b);
if (c == "-")
return (a - b);
if (c == "*")
return (a*b);
if (c == "/")
return (a / b);
}
如此反復,直至隊列為空。切記,訪問隊列元素之后,要將其刪除,否則或是無法輸出,或是結果出錯,貽害無窮,余深受其害,特告知諸君。此處不貼代碼,下面一起貼出。
七、會處理括號
括號,運算之君王也,萬事以其為先。然此子狡詐異常,余甚為頭疼,只得以鄉村土法治之。遇括號,則取其中元素於另一棧中,再進行計算。如此敘述較為抽象,以下將連上面代碼一起給出,內容略長,望諸君耐心詳讀,其中良莠,諸君自判,如有妙法,請傳授於余,余感激不盡。
while (!que.empty()) //隊列不為空則進入
{
if (que.front() == "(") //對括號內的部分進行處理(開始)
{ //遇到左括號,壓入
s_str.push("(");
que.pop();
}
else if (que.front() == ")") //遇到右括號
{
que.pop();
while (s_str.top() != "(") //把字符棧里的符號彈出,壓入一個隊列
{
q_temp.push(s_str.top());
s_str.pop();
}
s_str.pop();
while (!q_temp.empty()) //進行計算
{
f = s_num.top();
s_num.pop();
e = s_num.top();
s_num.pop();
g = q_temp.front();
q_temp.pop();
if (g == "/"&&f == 0) //除數是否為零的判斷
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
t = calculate1(e, g, f); //進行計算
if (length_check(t) == 0) //數字長度的判斷
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
s_num.push(t); //把結果壓入數字棧
}
} //(結束)
else if (que.front() == "+" || que.front() == "-" //對括號外部分的計算
|| que.front() == "*" || que.front() == "/")
{
if (s_str.empty() || s_str.top() == "(")
{
s_str.push(que.front());
que.pop();
}
else
{
if (priority(que.front()) > priority(s_str.top()))
{
s_str.push(que.front());
que.pop();
}
else
{
g = s_str.top();
s_str.pop();
f = s_num.top();
s_num.pop();
e = s_num.top();
s_num.pop();
if (g == "/"&&f == 0)
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
t = calculate1(e, g, f);
if (length_check(t) == 0)
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
s_num.push(t);
}
}
}
else
{
stream.clear();
stream << que.front();
stream >> change;
s_num.push(change);
que.pop();
}
}
while (!s_str.empty())
{
f = s_num.top();
s_num.pop();
e = s_num.top();
s_num.pop();
g = s_str.top();
s_str.pop();
if (g == "/"&&f == 0)
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
t = calculate1(e, g, f);
if (length_check(t) == 0)
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
s_num.push(t);
}
final_result = s_num.top();
return final_result;
}
以上內容和代碼為本菜鳥之愚知拙見,如有不當和錯誤之處,純屬正常,望諸君開慧眼識不足,並啟金口傳於余,余定當感激不盡。