1.10a – 如何设计你的第一个程序
from http://www.learncpp.com/cpp-tutorial/1-10a-how-to-design-your-first-programs/
將原文中的建議提到前面來講了:
{ //begin advise
編寫程序時的幾個建議
保持你的程序有一個簡單的開始。新手通常對于它們想要編程實現的內容有很大的野心?!拔蚁胍獙懸粋€角色扮演的游戲,有圖像,有聲音,隨機的怪物和地下城,有一個城鎮,你能夠訪問并賣掉從地下城獲取的東西”,如果你從一開始便嘗試寫一個復雜的東西,是將會被嚴重的打擊到,從而失去信心。相反,將你的第一個目標設置成足夠的簡單,一些你能夠完成的任務。舉個例子,“我想要在屏幕顯示2d表征世界的內容”。
?
隨著時間的推進,添加新的內容。當你實現你的簡單的程序,并且能夠很好的運行的時候,你應該添加新的特征。舉個例子,一旦你實現顯示一個2d世界,添加一個能夠走動的特征。當你能夠走動的時候,添加墻壁能夠阻止你前進。但你有了墻后,建立一個簡單的城鎮。當你建立了一個簡單的城鎮后,添加商人。通過不斷向你的程序中添加新的特征,能夠逐漸使你的程序具有的功能越來越多,并且不會打擊你的信心。
?
一次注意一個方面。不要嘗試一次性編寫很多東西,不要將你的注意力分散到繁雜的任務上。一次關注一個任務,盡可能充分的完成它。一個完整的工作任務的完成相比于6個部分的工作任務的開始要好很多。如果你分散了你的注意力,你很有可能犯錯誤,忘記一些重要的細節。
?
測試你的每一部分代碼。新手通常一次性編寫完整的程序。然后,他們開始進行第一次編譯,編譯器產生出成百上千的錯誤。這不是用來嚇唬人的,如果你的代碼沒有工作,很難夠指出具體問題在什么地方。相反,寫一部分代碼,然后立刻編譯測試它。如果它不能工作,你會確切的知道具體問題出在什么地方,然后修復它。一但確定你的代碼能夠正確運行時,開始編寫下一段,重復上述步驟。它也許會花費你很長時間完成你的代碼,但是,當你完成你的代碼時,整個程序應該是能夠執行的,你也沒有必要花費更長的時間試圖去指出它為什么會犯錯。
?
大多數新手會快速略過這些步驟和建議,因為它看上去花費了很多工作,并且也沒有像寫代碼那么有趣。但是,對于任何一個重大的項目,遵從這些步驟,將會節約你很大一部分時間。開始一點的計劃能夠節省后來很多的調試。
在你適應這些概念之時,他們會自然而然的出現。最后,你能夠在沒有實現計劃下寫出所有函數。
} // end advise
?
?
?
現在,你已經學習了關于編程的基本內容,讓我們更進一步學習如何設計一個程序吧。當你坐著編程的時候,通常你會有一些不同的問題需要解決,或一些狀況你想要模擬。初學者在將思想轉變成實際的代碼上通常會有困難。但是事實上呢,你有很多從日常生活中獲取的解決問題的技能。
需要記住的最重要的事情是在你開始編寫代碼之前設計你的程序。編程就好比建筑學。沒有一個良好的建造計劃,你便開始建造房子,那么將會發生什么情況呢?事實是這樣的,除非你是天才,你建造的房子總會有一大堆的問題:屋頂漏水,墻壁不直等等。類似的來說,在沒有很好的計劃之前便開始編寫你的程序,你將會發現你的代碼具有很多的問題,因此你不得不花費大量的時間去修正這些通過一些設計本來能夠避免的問題。
簡單的預先計劃將會減少你時間和遇到問題時的挫敗感。
?
1. 定義一個問題
你需要指出的第一件事情是確切地明白你的程序是用來解決什么問題。理想狀態上,你應該用一兩句話進行陳述。舉個例子:
?
- * 我想要寫一個電話本的應用程序幫助我記錄我的朋友電話號碼。
- * 我想要寫一個隨機空洞生成器,能夠產生看起來有趣的洞穴(這一句可能翻譯的有問題,原句為:I want to write a random dungeon generator that will produce interesting looking caverns.)
- * 我想要寫一個程序,能夠利用股票的信息,嘗試著去預測我可以選那一股。
盡管,這一步看上去好像是很明顯的,但是它還是很重要的。你能夠遇到的最糟糕的事情就是你寫的程序并沒有實現你想要實現的功能。
?
2. 定義你的目標
當你是一個有經驗的程序員的時候,這一步能夠被分解為很多細小的步驟,包含:
- * 明白你的目標用戶是誰
- * 定義你的程序將在哪種目標架構或操作系統上運行
- * 決定哪些工具你將會使用到
- * 決定你將一個人還是一個團體編寫程序
- * 收集需求(一個具有一些列程序將做什么的文檔)
但是,作為一個新手,對于這些問題的答案,往往是簡單的:你寫的程序僅僅你自己使用,在你的系統上,使用你購買或是下載下來的IDE。這使得事情變得簡單,因此我們在這一步上不會花太多的時間。
?
3. 確定任務的子任務
在現實生活中,我們通常需要完成非常復雜的問題。試圖指出如何完成這些任務是很有挑戰的。同樣,我們通常采用由上至下的方法來解決問題。也就是說,不像解決一個單一的問題,我們將一個任務分解為很多子任務,每一個部分都是很容易就能夠解決的。如果這些子任務仍然很難被解決,他們能夠被進一步的分解。不停的將復雜的任務分解為簡單的部分,你最后會得到能夠容易解決的各個問題,并且不至于瑣碎。
讓我們來看一下例子。加入我們要寫一個關于胡蘿卜的報告。我們的任務像這個樣子:
* 寫一個關于胡蘿卜的報告
寫一個關于胡蘿卜的報告是一個很大的任務,因此讓我們細分它+
* 寫一個關于胡蘿卜的報告
??????? o 對胡蘿卜進行研究
??????? o 寫一個大綱
??????? o 用關于胡蘿卜的詳細信息填充大綱
這樣變得更加容易執行,我們已經將一個問題分解成了三個任務。但是,在這個例子中,“對胡蘿卜進行研究是模糊不清的”,因此我們繼續分解:
* 寫一個關于胡蘿卜的報告
??????? o 對胡蘿卜進行研究
??????????????? D 去圖書館獲取關于胡蘿卜的書
??????????????? D 在網上搜索關于胡蘿卜的信息
??????? o 寫一個大綱
??????????????? D 關于成長的信息
??????????????? D 關于加工的信息
??????????????? D 關于營養的信息
??????? o 用關于胡蘿卜的詳細信息填充大綱
現在我們有了任務的列表,并且沒有一個是特別困難的。通過完成每一個相對簡單的子任務,我們能夠完成一個更復雜的問題。
?
另一個建立層次的方法是采用自底向上的方法。我們通過這個方法能夠將一些列簡單的任務,通過歸類、組織,分門別類。
作為一個例子,很多人在工作日不得不工作或是上學,因此假定我們系那個要解決的問題是“起床去工作”。如果,每天早上從床上爬起來的過程中你做了什么事,你也許會想到一系列事情:
D 挑選要穿的衣服
D 穿上衣服
D 吃早餐
D 開車去上班
D 刷牙
D 爬起床
D 準備早餐
D 進入你的車
D 洗個澡
將上面的過程進行分門別類得到如下的結果:
* 起床去工作
??????? o 臥室相關事情
??????????????? D 爬起床
??????????????? D 挑選要穿的衣服
??????? o 浴室相關事情
??????????????? D 洗個澡
??????????????? D 刷牙
??????? o 早餐相關事情
??????????????? D 準備早餐
??????????????? D 吃早餐
??????? o 交通相關事情
??????????????? D 進入你的車
??????????????? D 開車去上班
這種分層次的方法在編程中被證明是很有效的,因為一旦當你有一個任務層次,本質上你同時定義了你的程序的結構。最頂層的任務成為main()。子任務可以是程序中的函數。
如果證明子任務中的一項很難被實現的時候,通常繼續將這個子任務進行細分,讓一個函數通過調用實現新任務的不同的子函數。最后你會實現這個程序。
?
4. 指出事情發生的序列
現在你的程序有了一個結構,到了將這些任務組織到一起的時候了。首先需要確定事件發生的先后序列。舉個例子,當你早上起床的時候,你完成那些事情的時候具體的順序是什么樣的呢?可能是這樣的:
* 爬起床
* 挑選要穿的衣服
* 洗個澡
* 穿上衣服
* 準備早餐
* 吃早餐
* 刷牙
* 進入你的車
* 開車去上班
如果寫一個計算器,我們會采用下面的順序:
* 獲取用戶輸入的第一個數據
* 獲取用戶輸入的操作符
* 獲取用戶輸入的第二個數據
* 計算結果
* 輸出結果
這些一系列定義在函數中表現如下:
1: int main() 2: { 3: GetOutOfBed(); 4: PickOutClothes(); 5: TakeAShower(); 6: GetDressed(); 7: PrepareBreakfast(); 8: EatBreakfast(); 9: BrushTeeth(); 10: GetInCar(); 11: DriveToWork(); 12: }?
或是計算器的例子:
1: int main() 2: { 3: // Get first number from user 4: GetUserInput(); 5: 6: // Get mathematical operation from user 7: GetMathematicalOperation(); 8: 9: // Get second number from user 10: GetUserInput(); 11: 12: // Calculate result 13: CalculateResult(); 14: 15: // Print result 16: PrintResult(); 17: }?
?
5. 指出每一個任務中需要輸入和輸出的數據
當你有事件的層次,和事件的具體序列的時候,接下去的事情是指出每一個任務需要輸入哪些數據,以及產生哪些數據。如果你已經從前面的步驟中獲取了輸入的數據,那么數據的數據將編程參數,如果你在其他的函數中計算得到結果,那么輸出結果會是一個返回值。
當我們完成后,我們應該有每一個函數的原型。如果你已經忘記了,函數原型是包含函數名字,參數,返回類型的函數的聲明,但是沒有具體的實現。
讓我們舉個例子,GetUserInput()是相當的直接的。我們將獲取用戶輸入的數據,并通過return返回給調用的函數。我們已經知道了各個序列之間的關系,獲取得到的數據將會是其他函數的參數。通過CalculateResult()來計算結果,返回結果,但是它并不能自己顯示結果。因此,我們需要返回這個值讓其他的函數使用。
對于CaculateResult函數原型是這樣的:
1: int CalculateResult(int nInput1, char chOperator, int nInput2);?
6. 編寫每個任務的詳細部分
在這個步驟,我們將編寫每個任務具體的實現。如果你將任務劃分的足夠小,每一個小任務都將是非常簡單,容易實現的。如果一個任務看上去還是挺復雜的,它就需要進一步的劃分成一些容易實現的小任務。
舉個例子:
1: char GetMathematicalOperation() 2: { 3: cout << "Please enter an operator (+,-,*,or /): "; 4: 5: char chOperation; 6: cin >> chOperation; 7: 8: // What if the user enters an invalid character? 9: // We'll ignore this possibility for now 10: return chOperation; 11: }?
?
7. 將輸入和輸出聯系到一起
最后就是用一種合適的方式將輸入與輸出聯系到一起。舉個例子,CalculateResult函數的結果傳到PrintResult函數,因此能夠打印出結果。這通常包含中介變量的使用,用來暫時存儲結果,方便函數間的傳遞。
1: // nResult is a temporary value used to transfer the output of CalculateResult() 2: // into an input of PrintResult() 3: int nResult = CalculateResult(nInput1, chOperator, nInput2); 4: PrintResult(nResult);這種方式會比下面的代碼更容易讀:
1: PrintResult( CalculateResult(nInput1, chOperator, nInput2) );?
對于新手通常是最不好處理的一步。
完整的計算程序可以自己實現,答案間原文。
?
轉載請注明來自:http://grass-and-moon.cnblogs.com
轉載于:https://www.cnblogs.com/grass-and-moon/archive/2012/04/29/2476584.html
總結
以上是生活随笔為你收集整理的1.10a – 如何设计你的第一个程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 项目方面的做人处事
- 下一篇: 8086之8253芯片仿真