在討論算法時,我們列出了順序、選擇、循環三個控制過程,這是結構化編程方法強調的三個基本結構。算法的執行過程由壹系列操作組成,這些操作之間的執行順序就是程序的控制結構。1996年,計算機科學家Bohm和Jacopini證明了壹個事實:任何簡單或復雜的算法都可以由三種基本結構組成:序列結構、選擇結構和循環結構。所以這三種結構被稱為編程的三種基本結構。也是結構化編程中必須采用的結構。
1.序列結構
順序結構表示程序中的操作按照出現的順序執行,流程如圖1-6所示。圖中的S1和s2表示兩個處理步驟,可以是壹個非轉移操作或多個非轉移操作序列,甚至可以是空操作,也可以是三種基本結構中的任意壹種。在序列結構中只有壹個入口點A和壹個出口點B。這種結構的特點是程序從入口點A開始,按順序執行所有操作,直到出口點B,所以叫順序結構。上壹節的圖1-2顯示了壹個序列結構的流程圖。事實上,無論程序中包含哪種結構,程序的整體流程都是序列結構的。例如,在圖1-3、圖1-4和圖1-5所示的流程圖中,整體結構流程從上到下依次執行。
選擇壹種結構
選擇結構表示程序的處理步驟中有分支,它需要根據壹定的條件選擇其中的壹個來執行。選擇結構有三種形式:單項選擇、雙項選擇和多項選擇。
雙選是典型的選擇結構,其流程如圖1-8所示,其中s1和s2與序列結構中的相同。從圖中可以看出,在結構的入口點A有壹個判斷框,表示程序流中有兩個可供選擇的分支。如果條件滿足,則執行s1處理,否則執行s2處理。值得註意的是,這兩個分支只能選擇和執行其中壹個,但無論選擇哪個分支執行,最終的流程肯定會到達結構的出口點B。在前面的圖1-3中,采用了雙選結構的流程圖。
當s1和s2的任壹處理為空時,意味著結構中只有壹個分支可供選擇。如果滿足條件,將執行s1處理,否則將按順序進入流程出口B。也就是說,當條件不滿足時,什麽都不執行,所以稱為單選結構,如圖1-7所示。
多選結構意味著很多分支如s1,s2、...,sn如圖1-9在程序流程中遇到,會根據條件確定程序執行方向。如果滿足條件1,則執行s1的處理,如果滿足條件n,則執行Sn的處理。簡而言之,應該根據判斷條件選擇多個分支中的壹個來執行。無論選擇哪個分支,最終的流程都會到達同壹個出口。如果所有分支機構的條件都不滿足,就直接去出口。有些編程語言不支持多選結構,但所有結構化編程語言都支持。c語言是壹種面向過程的結構化編程語言,可以非常簡單地實現這個功能。在第五章,本書將詳細介紹各種形式的選擇結構的應用。
3.環狀結構
循環結構是指程序重復執行壹個或壹些操作,直到某個條件為假(或真)才終止循環。循環結構中最重要的是:什麽時候執行循環?哪些操作需要循環執行?循環結構有兩種基本形式:when型循環和until型循環,其流程如圖1-10所示。圖中虛線框內的操作稱為循環體,是指從循環入口點A到循環出口點B的處理步驟,是需要循環執行的部分。以及何時執行循環取決於條件。
類型結構:表示先判斷條件,當滿足給定條件時,執行循環體,進程在循環終點自動返回循環入口;如果條件不滿足,退出循環體,直接到達流程的出口。因為是“條件滿足時循環”,即先判斷後執行,所以叫循環。流程如圖1-10(a)所示。
Until型循環:是指直接從結構入口開始執行循環體,在循環結束時判斷條件。如果條件不滿足,則返回入口繼續執行循環體,然後退出循環,直到條件為真,到達流程的出口。先執行,再判斷。因為是“直到條件為真”,所以稱為until-type循環。流程如圖1-10(b)所示。本章圖1-5的流程圖,用叠代法求和,是典型的until型循環結構。
同樣,循環結構只有壹個入口點A和壹個出口點B,循環終止意味著進程在循環的出口點執行。圖中表示的過程可以是壹個或多個操作,也可以是壹個完整的結構或過程。
整個虛線框是壹個圓形結構。
通過三個基本控制結構可以看出,結構化程序中的任何基本結構都有唯壹的入口和出口,程序不會出現無限循環。程序的靜態形式和動態執行流程之間有很好的對應關系。
1.3.2南北流程圖
N-S流程圖是結構化編程方法中用來表示算法的圖形工具之壹。對於結構化程序設計,傳統的流程圖已經很難完全適應。因為傳統的流程圖出現的比較早,更多的反映了機器指令系統設計的需要和傳統的編程方法,很難保證有壹個好的程序結構。另外,結構化程序設計的壹些基本結構在傳統的流程圖中沒有相應的表達符號。比如在傳統的流程圖中,循環結構仍然是用判斷結構符號來表示,這樣就很難區分是哪種結構。尤其是傳統的流程圖因為轉折問題,無法保證自頂向下的編程方式,難以表達模塊間的調用關系。為此,美國兩位學者Nassi和Schneiderman在1973中提出了壹種新的流程圖形式,即N-S流程圖,以兩位創造者的姓名首字母命名,也稱為Nassi Shneiderman圖。
N-S圖的基本單元是壹個矩形框,它只有壹個入口和壹個出口。矩形框由不同形狀的線條劃分,可以表示順序結構、選擇結構和循環結構。在N-S流程圖中,完全去掉了有方向的流線,用三種矩形框表示程序的三種基本結構,可以組合起來表示所有的算法。這種流程圖從表達形式上消除了任意使用控制轉移對程序流程的影響,限制了不良程序結構的產生。
順序、選擇、循環三種基本結構對應的N-S流程圖的基本符號如圖1-11所示。圖1-11(a)和圖1-1 (b)分別是序列結構和所選結構的N-S圖,圖1-11。從圖中可以看出,在N-S圖中,流程總是從矩形框的頂部開始,壹直延續到矩形框的底部,這是流程的入口和出口。在這種形式下,無條件轉讓是不可能的。接下來用N-S流程圖展示上例1-2中求函數值m的算法,其流程圖如圖1-12所示。
值得註意的是,N-S流程圖是壹種適用於結構化程序設計方法的圖形工具,但對於非結構化程序,不能用N-S流程圖來表示。
比如例1-3,求任意兩個正整數的最大公約數的算法,就非常經典。算法用圖1-5的傳統流程圖表示,但不能直接用N-S流程圖表示。因為這個算法的關鍵是執行壹個循環結構,但是圖1-5所示的循環結構既不是當前循環,也不是till循環,所以不能用N-S流程圖來表示。如果將例1-3中的算法稍作調整,使流程圖采用單選結構的形式,並將其中的條件改為r≠0,則該算法可以用until型循環的N-S流程圖來表示。圖1-13是示例1-3的N-S流程圖。
N-S流程圖是描述算法的重要圖形工具之壹,在結構化程序設計中得到了廣泛應用。這裏只是簡單介紹壹下,旨在引玉。在實際的軟件開發中,有興趣的讀者可以參考軟件工程或軟件開發技術方面的著作。
1.3.3結構化編程方法
結構化編程方法是公認的面向過程編程應該遵循的基本方法和原則。結構化編程方法主要有:①只用三種基本的程序控制結構來編譯程序,使程序具有良好的結構;②自頂向下編程;(3)用結構化程序設計流程圖來表示算法。
關於結構化編程和方法有壹套理論和技術,在不斷發展和完善。對於初學者來說,很難完全掌握。但在學習初期,了解結構化編程的方法,學習好的編程思想,是很有幫助的。
1.結構化編程特征
結構化編程的特點主要包括以下幾點:
(1)程序由三個基本結構的組合來描述;
(2)整個程序采用模塊化結構;
(3)限制性地使用transfer語句,在絕對必要時要非常謹慎,只在壹個結構內跳轉,不允許從壹個結構跳轉到另壹個結構,以縮小程序靜態結構和動態執行過程的差別,使人們正確理解程序的功能;
(4)以控制結構為單元,每個結構只有壹個入口和壹個出口,單元間接口簡單,邏輯清晰;
(5)使用結構化編程語言編寫程序,采用壹定的編寫格式,使程序結構清晰易讀;
(6)註意編程風格。
2.自頂向下的設計方法
結構化編程的總體思路是采用模塊化結構,自上而下,逐步完善。即首先把壹個復雜的大問題分解成幾個相對獨立的小問題。如果小問題還是比較復雜,可以繼續分解成幾個子問題,這樣小問題或者子問題就可以簡單的用程序的三個基本結構來表達了。然後對應每個小問題或子問題,編寫壹個功能獨立的程序塊,像搭積木壹樣叫做模塊。每個模塊都是壹個壹個拆開,然後統壹組裝。這樣,壹個復雜問題的解就變成了幾個簡單問題的解。這是壹種自上而下、循序漸進的編程方法。
確切地說,模塊是程序對象的集合。模塊化就是把程序分成幾個模塊,每個模塊完成某個子功能,把這些模塊整合成壹個整體就可以完成問題的解決。這種用模塊組裝的程序稱為模塊化結構程序。在模塊化結構程序設計中,自頂向下、逐步細化的設計方法便於問題的分解和模塊的劃分,因此是結構化程序設計的基本原則。
例1-9:求壹元二次方程;
ax2+bx+c=0
的根。
解析:從頂層考慮,解題的算法可以分為三個小問題,即輸入問題、求根問題和輸出問題。這三個小問題是壹個二次方程求根的三個功能模塊:輸入模塊M1、計算處理模塊M2和輸出模塊M3。其中,M1模塊完成必要原始數據的輸入,M2模塊根據求根算法求解,M3模塊完成所得結果的顯示或打印。這種劃分使得壹元二次方程的求根問題成為三個相對獨立的子問題,其模塊結構如圖1-14所示。
這三個分解模塊壹般都是序列結構。其中,M1和M3模塊完成簡單的輸入輸出,無需分解即可直接設計程序流程。M2模塊完成求根,求根需要先判斷二次系數a是否為0。當a=0時,方程退化為線性方程,求根的方法與二次方程不同。若a≠0,則應根據b2-4ac求二次方程的根。可以看出,M2模是復雜的,可以細分為兩個子模M21和M22,分別對應壹次方程和二次方程的根。模塊結構如圖1-15所示。
經過這樣的分解,M21子模塊的作用是求壹個線性方程的根,其算法簡單,可以直接表示。M22是二次方程的根。流程圖表示的算法如圖1-16所示。它由壹個簡單的序列結構和壹個選擇結構組成,是M22模塊最精細的流程表示。然後按照細化M22模塊的方法,將M1、M21和M3的算法分別用流程圖表示,然後分別按照圖1-15和圖1-14的模塊結構進行組裝,最終會得到細化的完整流程圖。
可見編程和蓋樓是壹樣的。首先要考慮建築的整體結構,忽略壹些細節。整體框架搭建好之後,再逐步解決各個房間的細節問題。在程序設計中,首先考慮問題的頂層設計,然後逐步細化,完成底層設計。采用自頂向下、逐步求精的設計方法,符合解決復雜問題的壹般規律,是人們習慣接受的方法,可以顯著提高程序設計的效率。在這種自上而下,各個擊破的方法指導下,實現了從整體到局部,從整體到細節,從抽象到具體的逐步細化過程。這樣編寫的程序具有結構清晰的特點,提高了程序的可讀性和可維護性。
3.編程風格
從某種意義上說,編程風格是個人寫程序時的壹種習慣。但風格問題並不像方法問題那樣涉及壹套相對完善的理論和規則。編程風格是對編程經驗教訓的提煉,不同程度不同應用角度的程序員在這個問題上也有自己的看法。正因為如此,編程風格很容易被人們忽視,尤其是新手。結構化編程強調編程風格的要求。因為編程風格主要影響程序的可讀性。壹個具有良好風格的節目應該註意以下幾點:
(1)語句形式化。編程語言是壹種形式語言,需要準確無誤。所以,死板的形式,活潑的內容,才是軟件行業的風範;
(2)程序壹致性。保持程序所有部分的風格和文檔格式壹致;
(3)結構標準化。程序結構、數據結構甚至軟件架構都要符合結構化編程的原則;
(4)適當運用筆記。註釋是幫助程序員理解程序,提高程序可讀性的重要手段。註釋可以適當地添加到壹個程序或壹行程序中。
(5)標識符接近現實。程序中數據、變量和函數的命名原則是選擇有意義的標識符,便於識別和理解。避免使用模糊的縮寫和標識符。比如,盡量用V和I代替A和B作為代表電壓和電流的變量名。避免使用不直觀的變量名,如aa、bb等。