1. 程式人生 > >深入 自頂向下 逐步求精 面向過程程式設計方法

深入 自頂向下 逐步求精 面向過程程式設計方法

               

  程式設計初學者常常受困於不會想問題:“不知道讓計算機解決這個問題該如何做”。其實,程式設計師的一個基本功是,能夠將複雜的問題分解開來。學會分解任務,因超級大分為大的、中的、小的、超小的,直到能用很直接的方法解決。記住一個很管用的策略:自項向下,逐步求精。不管做何事,都拿這個策略套一套,程式能編好,其他事也都能做。

  本講的主要目標在於:

  (1)讓你建立“自頂向下,逐步求精”的思維方式,你的大腦先能“機械化”,為指揮計算機“一步一步”地完成任務打下基礎;

  (2)讓迴圈的概念在頭腦中紮根:在順序、選擇、迴圈三種控制結構中,迴圈是最複雜的(只要你掌握了,也就不復雜,而成為一種自然的思維方式了),也是解決問題的最有效的結構,我們就先在此力求先期得到體會。

  (3)C++程式的基本單位是函式,用函式解決更小的問題,這是求精實現的途徑。

  這部分內容並未對應到課本上的某一段。要學會“跳躍”的學習方式,自己構建知識,跳出所謂課本的框框。課本給出了一種知識的組織形式,然而知識本身並不本來就呈現這種關係的,這樣組織只是作者的一種思路而已。工科學生要學會查閱資料,因為你所需的知識“背”不會,也不應該是用“背書”的方式學的。將課本看成是手冊、工具書,無論學到哪兒,能隨時恰當、準確地找到書中能夠給你啟示和參考的地方。不要迷信老師講過就如何如何,只要你需要,消除心理障礙,你就能讀下去,獲得自己需要的知識。

  先探討一個最典型的用迴圈解決的問題。重點是從中體會用多種方式表示對同一個問題的解決。實際上,我們需要做到的是,很熟練地用隨便一種方式將腦袋裡形成想法表達出來。   

  【例1】求1+2+3+…+100

  解題思路為:

  • 設:和用sum表示,迴圈100次,第i次迴圈將i累加到sum
  • sum初值為0;
  • i初始為1,每迴圈一次增加1,直到100

  下面是解決這個問題的演算法的不同描述形式:

           

  用while迴圈語句寫的程式

#include <iostream>using namespace std;int main( )int i=1,sum=0while (i<=100) { sum=sum+i;   i++; } cout<<"sum="<<sum<<endlreturn 0;}
  用for迴圈語句寫的程式
#include
<iostream>
using namespace std;int main( )int i,sum=0for (i=1;i<=100;i++)   sum=sum+i; cout<<"sum="<<sum<<endlreturn 0;}
  後面遇到的問題,會用一種形式寫出來了,學著用其他方式寫一寫,用不了多久,腦子就活起來。

  下面,我們用例子體驗開始“自頂向下,逐步求精”。如果你現在已經能順利編出這些程式,也需要再次體會這種思維。

  【例2】在螢幕上輸出如圖所示的星號圖形

  

  程式設計師不能只看到一堆星星,要用分解的思想,整理出關於圖的規律來,或者說,會分層次地看這個問題。這要訓練“自頂向下,逐步求精”的思維方式。

  思路:首先看到的是一個圖;這個圖有6行;每一行有若干個星號。可喜的是,每一行星號的個數還有規律:第i行星號的個數是2*i-1個!

  演算法就出來了:

輸出6行星號
  這是我們“自頂向下,逐步求精”的第一次分解:將一個圖分成了若干行;  輸出6行用一個迴圈結構解決,迴圈每執行1次就輸出1行,寫成虛擬碼是:
//程式片段(1)i=1;While(i<=6){  輸出第i行;  換行  i=i+1;}
  “輸出第i行”的問題還需要分解下去。實際上,輸出“輸出第i行”就是要“重複輸出2*i-1個星號”,也用一個迴圈結構完成。
//程式片段(2)j=1;While(j<=2*i-1){ 輸出一個*; j=j+1;}
  將程式片段(1)中的“輸出第i行”替換為程式片段(2),整個演算法也就清晰了。
i=1;While(i<=6){  j=1;  While(j<=2*i-1)  {    輸出一個*;    j=j+1;  } 換行 i=i+1;} 
  用圖示再表達一次上述過程,體會“自頂向下,逐步求精”。

  我們可以寫出完整的演算法,據此寫程式也就容易了。無論用while語句,還是for語句。

//用while迴圈的程式#include <iostream>using namespace std;int main( ){  int i,j,n=6;//n可以賦別的值  i=1;  while(i<=n)  {    j=1;    while(j<=2*i-1)    {      cout<<'*';      j++;    }    cout<<endl;    i++;  }  return 0;}

  體會本例的任務:將輸出一個“星號組成的圖案”,分解為“迴圈輸出若干行”,找出各行的規律,能夠逐步細化到“輸出第i行”這個任務,最終問題細化到只輸出一個字元’*’。從“頂層”出發,“向下”(即程式設計中能直接實現的細節)考慮,“逐步”地“求精”得到達到用C++語句直接描述的程度。對複雜的問題,可能需要更多層次的分解。想一想,我們做任何工作,對大學生活做一巨集觀的規劃、制定一天的學習計劃、組織一次班級活動、將來的工程專案開發、做老闆後策劃一項商業活動、做官員後……無論複雜還是簡單,有意或無意地,都是在“自頂向下,逐步求精”的偉大思想的光芒中行走的。只是現在,你需要用心體會,將其成為你的思維慣性。

  好了,回到技術層面,請用N-S圖和流程圖表示輸出星號圖的演算法。直接用程式碼的形式在解決大問題時並不總是有效,學會這種表達方法仍是必要的。我們在實踐中學習。

  接下來,進一步體驗讓“自頂向下,逐步求精”更加燦爛的程式部件——函式。請閱讀下面的兩個函式的定義:

[cpp] view plain copy print?
  1. <code class="language-cpp">//(1)定義能輸出一行m個星號的函式
  2. void printstar(int m)  
  3. {   
  4.   int j=1;  
  5.   while(j<=m)  
  6.   {  
  7.     cout<<'*';  
  8.     j++;  
  9.   }  
  10.   cout<<endl;  
  11. }</code>  
//(1)定義能輸出一行m個星號的函式void printstar(int m){   int j=1while(j<=m)  {    cout<<'*';    j++;  }  cout<<endl;}
//(2)定義在當前行能輸出若干指定符號的函式void printch(char c, int m){   int j=1while(j<=m)  {    cout<< c;    j++;  }  cout<<endl;}
  讀懂這段程式碼應該已經不算難事。接下來,試著在程式中,引入某個函式為你服務吧。比如,可以像下面的程式:
//while迴圈中呼叫函式的程式#include <iostream>using namespace std;int main( )int i,n=6;  i=1while(i<=n)  {    printstar(2*i-1);//呼叫函式    i++;  }  return 0;}
  這段程式碼的功能與前面完全一樣,但由於用函式printstar(2*i-1);完成若干個星號的輸出,顯然,程式簡潔了很多。

  還可以這樣寫:

//while迴圈中呼叫函式的程式#include <iostream>using namespace std;int main( )int i,j,n=6for(i=1;i<=n;i++)    printch('*',2*i-1);  return 0;}
  這時,功能與上述是一樣的,換了種迴圈語句,程式更短些,更重要的是,for迴圈適合於這種迴圈次數固定的問題。“計數型”迴圈用for,要習慣這樣。

  另外,printch('*',2*i-1);要換成printch('#',2*i-1);呢?輸出的是'#'號圖!我們可以很方便地輸出其他符號的圖。函式就是專門為完成一些通用任務而設的,函式就是為“自頂向下,逐步求精”而生的。

  在面向過程的結構化程式設計中,“模組”是構成程式的基本單元,好比是一座大樓中的各個房間,有辦公室、實驗室、會議室、門房,各自獨立,但共同組成了大樓。有了這樣的模組,程式不需要寫得很長,將一段功能獨立的程式碼寫成一個函式,讓別的函式呼叫就是了(冒昧地用上術語了,定義的函式是要被呼叫的,於是有了主調函式和被調函式,如例子中,主調函式是main(),被調函式,你懂的)。在對問題分解後,並不一定需要把各級分解的結果堆在一起,組裝出一個長長的貌似高水平的程式。“簡單”是工程中的第一法則,用函式構造出的程式,結構簡單、易讀,易於多人合作分工完成,好處是不容易出錯,將來的維護也就容易了。

  本節練習:輸出各種星號圖。針對任務設計演算法,並畫出流程圖。實現時,可以編出不使用函式和使用函式的兩種版本。

  每做出一個程式,停下來再看一看程式,默唸“自頂向下,逐步求精”。