標籤:
構造演算法與自上而下逐步完善:執行個體研究2(標記控制重複)
下面將全班平均成績問題一般化,考慮如下問題:
開發一個計算全班平均成績的程式,在每次程式運行時處理任意個成績數。
在第一個全班平均成績例子中,成績個數(10)是事先預置的。而本例中,則不知道要輸入多少個成績,程式要處理任意個成績數。程式怎麼確定何時停止輸入成績呢?何時計算和列印全班平均成績呢?
一種辦法是用一個特殊值作為標記值(sentinelvalue),也稱訊號值(signalvalue)、啞值(dummy value)或標誌值(flag value),表示資料輸入結束(“end of data entry”)使用者輸入成績,直到輸入所有合法成績。然後使用者輸入一個標記值,表示最後一個成績已經輸入。標記控制重複(sentinel-controlled repetition)也稱為不確定重複(indefinite repetition),因為執行迴圈之前無法事Crowdsourced Security Testing道重複次數。
顯然,標記值不能與可接受的輸入值混淆起來。由於考試成績通常是非負整數,因此可以用-1作標記值。這樣,全班平均成績程式可以處理95、96、75、74、89和-l之類的輸人流。程式計算並列印成績95、96、75、74和89的全班平均成績(不計入-1,因為它是標記值)。
常見編程錯誤2.8
將選擇的標記值與可接受的輸入值混淆時會造成邏輯錯誤。
我們用自上而下逐步完善(top-down,stepwise
refinement)的方法開發計算全班平均成績的程式,這是開發結構化程式的重要方法。我們首先產生上層虛擬碼表示:
Determine the class avcraqe for the quiz
上層虛擬碼只是一個語句,表示程式的總體功能。這樣.上層等於是程式的完整運算式。但上層通常無法提供編寫C++程式所需的足夠細節。因此要開始完善過程。我們將上層虛擬碼分解為一系列的小任務,按其需要完成的順序列出。這個結果就是下列第一步完善(first,refinement):
Initialize variables Input,sum,and count the quiz grades Calculate and print the class average
這裡只用了順序結構,所有步驟按順序逐步執行。
軟體工程視點2.4
上層虛擬碼及每一步完善都是演算法的完整定義,只是詳細程度不同而已。
軟體工程視點2.5
許多程式可以在邏輯上分為三個階段:初初化階段將程式變數初始化,處理階段輸入資料值和相應調整程式變數,結束階段計算和列印最後結果。
上述“軟體工程視點”通常是自上而下過程第一步完善的全部工作。要進行下一步完善(即第二步完善,second
refinement),我們要指定特定變數,要取得數位動態和以及電腦處理的數值個數,用一個變數接收每個輸入的成績值,一個變數儲存計算平均值。下列虛擬碼語句:
Initialize variables
可以細化成:
Initialize total to zero Initialize counter to zero
注意,只有total和counter變數要先初始化再使用,average和grade變數(分別計算平均值和使用者輸入)不需要初始化.因為它們的值會在計算或輸入時重定義。
下列虛擬碼語句:
Input, sum, and count the quiz grades
需要用重複結構(即迴圈)連續輸入每個成績。由於我們事先不知道要處理多少個成績,因此使用標記控制重複。使用者一次一項地輸入合法成績。輸入最後一個合法成績後,使用者輸人標記值。程式在每個成績輸入之後測試其是否為標記值.如果使用者輸入標記值,則順序迴圈終止。上述虛擬碼語句的第二步完善如下:
Input the first grade (possibly the sentinel) While the user has not as yet entered the sentinel Add this grade into the running total Add one to the grade counter Input the next grade (possibly the sentinel)
注意,在這個虛擬碼中,我們沒有在while結構體中使用花括弧,只是在while下面將這些語句縮排表示它們屬於while。虛擬碼只是非正式的程式開發協助工具輔助。
下列虛擬碼語句可以完善如下:
If the counter is not equal to zero Set the average to the total divided by the counter Print the average else Print "No grades were entered"
注意我們這裡要測試除數為0的可能性,這是個致命邏輯錯誤,如果沒有發現,則會使程式失敗(通常稱為爆炸或崩潰)。圖2.8顯示了全班平均成績問題第二步完善的完整虛擬碼語句。
常見編程錯誤2.9
除數為0是個致命邏輯錯誤。
編程技巧2.9
進行除法時,要測試除數為0的可能性,並在程式中進行相應處理(如列印一個錯誤訊息).而不是讓致命邏輯錯誤發生。
圖2.6和圖2.8的虛擬碼中增加了一些空行,使虛擬碼更易讀。空行將程式分成不同階段。
圖2.8所示的虛擬碼演算法解決更一般的全班平均成績問題,這個演算法只進行了第二步完善,還需要進一步完善。
Initialize total to zero Initialize counter to zero Input the first grade (possibly the sentinel) While the user has not as yet entered the sentinel Add this grade into the running total Add one to the grade counter Input the next grade (possibly the sentinel) if the counter is not rqual to zero Set the average to the total divided by the counter Print the average else Print "No grades were entered"
圖2.8 用標記符控制重複解決全班平均成績問題的虛擬碼演算法
軟體工程視點2.6
虛擬碼演算法的細節足以將虛擬碼變為C++代碼時,程式員即可停止自上而下逐步完善的過程,然後就可方便地實現C++程式。
圖2.9顯示了C++程式和樣本執行結果。儘管只輸入整數成績,但結果仍然可能產生帶小數點的平均成績,即實數。int類型無法表示實數,程式中引入float資料類型處理帶小數點的數(也稱為浮點數,floatingpoint number),並引入特殊的強制類型轉換運算子(cast operator)處理平均值計算。這些特性將在程式之後詳細介紹。
// Fig. 2.9: fig02_09.cpp// Class average program with sentinel-controlled repetition.#include <iostream.h>
#include <iomanip.h>int main(){int total, // sum of gradesgradeCounter, // number of grades enteredgrade; // one gradefloat average; // number with decimal point for average// initialization phasetotal = 0;gradeCounter = 0;// processing phasecout << "Enter grade, -1 to end: ";cin >> grade;while ( grade !=-1 ) {total = total + grade;gradeCounter = gradeCounter + 1;cout << "Enter grade, -1 to end: ";cin >> grade;}// termination phaseif ( gradeCounter != 0 } {average - static_cast< float >( total ) / gradeCounter;cout << "Class average is "<< setprecision{ 2 )<< setiosflags( ios::fixed | ios::showpoint )<< average << endl;}elsecout << "NO grades were entered" << endl;return 0; // indicate program ended successfully}
輸出結果:
Enter grade, -1 to end: 75 Enter grade, -1 to end: 94 Enter grade, -1 to end: 97 Enter grade,-1 to end: 88 Enter grade, -1 to end: 70 Enter grade, -1 to end: 64 Enter grade, -1 to end: 83 Enter grade, -1 to end: 89 Enter grade, -1 to end: -1 Class average is 82.50
圖2.9 用標記符控制重複解決全班平均成績問題的C++程式和樣本執行結果
注意圖2.9中while迴圈中的複合陳述式。如果沒有花括弧,則迴圈體中的最後三條語句會放到迴圈以外,使電腦錯誤地理解如下代碼:
while { grade ! = -1 ) total - total + grade; gradeCounter = gradeCounter + 1; cout << "Enter grade, -1 to end:"; cin >> grade;
如果使用者輸入的第一個成績不是-l,則會造成無限迴圈。
注意下列語句:
cin >>grade;
前面用一個輸出語句提示使用者輸入。
編程技巧2.10
提示使用者進行每個鍵盤輸入。提示應表示輸入形式和任何特殊輸入值(如使用者終止迴圈時輸入的標記值)。
編程技巧2.11
在標記控制迴圈中,提示請求輸入資料項目時應顯式指定標記值是什麼值。
平均值並不一定總是整數值,而常常是包含小數的值,如7.2或-93.5。這些值稱為浮點數,用資料類型float表示。變數average聲明為資料類型float,以獲得電腦結果中的小數。但total/gradeCounter的計算結果是整數,因為total和gradeCounter都是整數變數。兩個整數相除是整除(integer
division),小數部分丟失(即截尾,truncated)。由於先要進行計算,因此小數部分在將結果賦給average之前已經丟失。要用整數值進行浮點數計算,就要先產生用於計算的臨時浮點數值。
C++提供了一元強制類型轉換運算子(unary cast operator)。下列語句:
average = static cast< float >(total) /gradeCounter;
包括一元強制類型轉換運算子static_cast<float>(),產生用於計算的臨時浮點數值(total)。這樣使用強制類型轉換運算子稱為顯式類型轉換(explicit conversion)。total中存放的值還是整數,而計算時則用浮點數值(total的臨時float版本)除以整數gradcCounter。c++編譯器只能對運算元的資料類型一致的運算式求值。要保證運算元的資料類型一致,編譯器對所選擇的運算元進行提升(promotion)操作(也稱為隱式類型轉換,implicit conversion)。例如,在包含資料類型float和int的運算式中,int運算元提升為float。本例中,gradeCounter提升為float之後進行計算,將浮點數除法得到的結果賦給average。本章稍後將介紹所有標準資料類型及其提升順序。任何資料類型都可用強制類型轉換運算子,static_cast運算子由關鍵字statlc cast加角括弧(<>)中的資料類型名組成。強制類型轉換運算子是個一元運算子(unary perator),即只有一個運算元的運算子。第1章曾介紹過二元算術運算子。C++也支援一元正(+)、負(-)運算子,程式員可以編寫-7、+5之類的運算式。強制類型轉換運算子從右向左結合,其優先順序高於正(+)、負(-)運算子等其他一元運算子,該優先順序高於運算子*、/和%,但低於括弧的優先順序。優先順序表中用static_cast<type>()表示強制類型轉換運算子。
圖2.9中格式化功能將在第11章詳細介紹,這裡先做一簡要介紹。下列輸出語句中調用
setpreclslon(2): cout<<"Class average is" << setprecision(2) << Setiosflaqs(iOS::fixed |iOS::showpoint) << averaqe<< endl;
表示float變數average列印小數點右邊的位元為兩位精度(precision),例如92.37,這稱為參數化流操縱運算元(parameterized stream manipulator)。使用這些調用的程式要包含下列預先處理指令:
#include<iomanip.h>
注意endl是非參數化流操縱運算元(nonparameterized stream manipulator),不需要iomanip.h標頭檔。如果不指定精度,則浮點數值通常輸出六位精度(即預設精度,default precision),但稍後也會介紹一個例外。上述語句中的流操縱運算元setiosflags(ios::fixed |ios::showpoInt)設定兩個輸出格式選項ios::fixed和ios::showpoint。垂直條(1)分隔setiosflags調用中的多個選項(垂直條將在第16章詳細介紹)。選項ios::fixed使浮點數值以浮點格式(而不是科學計數法,見第ll章)輸出。即使數值為整數,ios::showpoInt選項也會強制列印小數點和尾部O,如88.OO。如果不用ios::showpoint選項,則C++將該整數顯示為88,不列印小數點和尾部o。程式中使用上述格式時,將列印的值取整,表示小數點位元,但記憶體中的值保持不變。例如,數值87.945和67.543分別輸出為87.95和67.54。
常見編程錯誤2.10
如萊在使用浮點敷時認為其精確地表示了敷值.則全得到不正確的蛄柬。浮點敷雇大多數電腦上都採用近似表示。
編程技巧2.12
不要比較浮點數值的相等和不等性,而要測試差值絕對值是否小於指定的值。
儘管浮點數算不總是100%精確,但其用途很廣。例如,我們說正常體溫98.6(華氏溫度)時,並不需要精確地表示,如果溫度計上顯示98.6度.實際上可能是98.5999473210643度。這裡顯示98.6對大多數應用已經足夠了。
另一種得到浮點數的方法是通過除法。10除以3得到3.333333……,是無限迴圈小敷。電腦只分配固定空間儲存這種值,因此只能儲存浮點值的近似值。
2016考研複試技巧http://www.kyjxy.com/fushi/zhinan/
考研專碩備考資料http://www.kyjxy.com/zhuanshuo/
考研院校政策http://www.kyjxy.com/yuanxiao/zhengce/
考研電腦基礎:構造演算法與自上而下逐步完善:執行個體研究2