3.3 初學者不能迴避——《逆襲大學》連載
返回到【全文目錄】
目錄
3.3 初學者不能迴避
開始學程式設計,一大堆的問題撲面而來。人類的認知非常厲害,當問題多時,常能夠避虛就實,優先將事情做下去。這也是學習中的一種非常寶貴的品質。在教學和諮詢中,我卻發現了一些本不該忽略的“虛”,卻使得需要的進步“實”不起來。有些困難對大學生而言,只是表面的東西,它們只是一隻紙老虎,只要突破心理上的畏懼和輕視,在程式設計進步過程中會走得更快和更好。
不要繞過英語
在日常的工作、學習中,我們用到很多的軟體都是英文介面。在學習中,我們需要很多用英文寫成的文件、資料。IT行業發展速度最快,對外開放的程度最高,與外界的交流最深入,學習英語,對計算機類專業的學生而言是當然的事。IT界最前沿的進展資訊都是用英語公佈的,很多的軟體沒有漢化版,絕大多數的優秀資料是不會翻譯成中文的(由於IT技術發展非常快速,即使有譯文,由於時間關係其時效性也不高了),中文譯本往往並不能很好地表達出原文的意思的,對有聲志於成為高階技術人才的學子,學好英語是必須的選擇。
有些同學知道英語對於程式設計學習的重要性,卻懷著所謂強烈的“民族自尊心”對英語心懷抵觸。用中文軟體,看中文書也就罷了,在實踐過程中直接需要用到英語了,卻主動繞道而行。最典型的一種表現,也是一種非常糟糕的做法是,除錯程式中遇到錯誤編譯或連線錯誤、警告,直接忽略給出的提示,甚至於錯誤發生在哪一行都不管,一頭扎到原始碼中瞪大眼睛,試圖找到問題所在。在我的教學中,我的大一學生在調程式遇到編譯錯誤時,總是直接忽略相關的提示資訊,抱怨著為什麼沒有中文提示,直接用眼睛在程式中尋找問題所在。這在程式設計的學習中,是一種直接廢了大半武功的做法!對於剛學程式設計的學生,盯著程式看再長的時間,哪有看一句“undeclared identifier”管用?當問及為何不看錯誤提示時,一句“看不懂”著實讓人心痛,他們在說出這三個字時無奈眼神更讓人心疼——這是學習了近十年英語的大學生!
有程式設計經驗的人都知道,有的提示資訊能直接告訴我們問題所在,有的不一定準確,但這是發現問題最重要的參考之一。可以根據提示發現出錯的線索,然而卻有眾多的初學者在不斷“碰運氣”中完成實踐,帶來了更多的受挫感,哪裡能夠收穫成就感?
於是,我在輔導上機時常和同學一起試著去讀一讀。個別的詞沒把握,線上詞典中一查,立馬解決。進而發現這句話原來很好懂,程式中犯的錯誤很低階。說“看不懂”的原因很簡單,只是因為沒有看。在錯誤提示剛一出現就暗示自己:我是看不懂英文提示的,對應的行為就是不看這些提示。再多問一句,“嘿嘿”之後,很自然地就是“英語不好”。
讓考試束縛了的學習啊,何時才能釋放出該有的力量!在選擇、填空、完型、改錯題中,封上了英語聽說讀寫能力提高的道路。話說養兵千日,用兵一時,然而在學了十多年的英語之後,到真正要用英語了,卻先給自己扣了個“英語不好”的大帽子,悄悄地溜了牆根。這是個舒適無比的溫柔的理由,從此不必再犯難改變英語的學習,不必再看那些洋文。一方面選擇從事了在全球化中站在最前沿的行業,另一方面卻在拒絕通向世界的這張通行證。
學程式設計序中遇到了英語,這正是在用中學英語的大好時機!去上英語課,採用通用的教學材料進行的學習,是我們慣常的學習,而在程式設計中遇到了應用中的英語,是一箭雙鵰的學習。不迴避標準化考試中受到的傷害,確實需要主動去挑戰一下那個所謂的心理障礙,沉下心來去讀一讀遇到的用英語表達的錯誤提示了。不這樣做,將永遠處在慌亂之中,嚴重影響程式設計學習的進度,程式設計中應該有的好感受中也總缺著一塊。沉浸在學英語就是做選擇題的圈子裡,不結合專業學習英語,不在用中去學英語,這就是一種自挖牆腳的做法,一副老態龍鍾,死氣沉沉的樣子。可能有的同學還在指望著還有一門叫做專業英語的課程,那又何必呢?這門課程需要修讀,但隨時都在使用著英語,這是學英語的最好機會。其實,束縛住自己的,永遠是自己。令人恐懼的,往往不是我們要面對的事物,而是自己的內心。內心封住了,英語學習就不敢往外走一步,不敢去執行“用中學”。
學生在程式設計中遇到的不認識的詞,不少是專業術語。用詞典查,常需要從諸多詞義中選擇一個,這往往並不是一件很容易的事。對於初學者,一些術語正在學習、理解當中,這個障礙自然不能忽略。不少技術類的中文書籍,在術語第一次出現的地方總是在括號中寫出對應的英語詞彙,然而同學們在看書時,括號裡的英語單詞是要直接忽略的。注意到這又是一個非常壞的習慣。上機實踐中不看英語提示,看書時再忽略專業術語的英語描述,在學習中諸多有血肉聯絡的內容,就這樣被生生地割裂開來了。
將程式設計的實踐與英語學習結合起來,用好程式設計環境中給出的英語提示,這是在初學程式設計時必須要正視的一個方面。很多事情,親自做了,不會有想象中的那樣難。遇到不會的詞,猜一下,或用線上詞典,或在課本上查一下,明白了,再做下去。看懂英文提示的過程,也是一個學習提高的過程,少捨棄一個本該面對的環節,得到的是雙倍的收穫。
我想將這個話題往程式設計之外稍稍擴充套件一下。大學生在用英語中,要有更多一些信心,也要安排機會,讓自己找到這種信心。學英語,一直是為著考試去的,而到用的時候卻逃了,在英語學習中曾經的投入真就是完全浪費了,你也將成為傳播“英語沒有用”觀點的最佳人選。憑著十來年學習英語的經歷,無論高考得了多少分,任何一個同學完全有能力去讀懂那些錯誤提示,只要去做,就能會。要讓自己進步更快,選一段時間,沉下心來,讀一讀經典的英文原著,進步會更快。如果想讓自己起步讀英語著作時的感受更好一些,不妨找你已經不存在知識障礙的原著起步。比如,在用中文教材學了C++語言後,讀一讀C++之父Bjarne Stroustrup著的《C++ Programming Language》。初時可能需要一點點強制,但只要讓自己完整地讀到了100頁,你會發現自己真的能讀下去,再看不少譯著真彆扭。越是經典的書,其中的英語的表達越簡單,越易讀。我們所缺少的,只是起步去做。
不要用一句“看不懂”讓自己避開,不要用一句“英語不好”封上自己通往更廣闊空間的路。英語學習貴在積累,在程式設計中遇到錯誤提示,這是避不開的事情,程式設計的能力就是在不斷糾錯中鍛煉出來的。如果將遭遇錯誤也看作是失敗的話,這種從失敗中的學習,永遠是最有效率的。
用好除錯工具
在編寫程式中,通過編譯的程式,只是說明其中已經沒有語法錯誤了。而在此時,我們將關注比語法錯誤更隱蔽、更危險的執行錯誤和邏輯錯誤。我們稱程式中錯誤為bug,而檢測並修正這些錯誤的方法就是debug。程式設計人員的日常工作,有大部分的時間花在debug上。在冗長的程式碼中,找出這些錯誤並不總是一件容易的事,但真正的程式設計師會把與bug戰鬥當成自己的神聖使命,用各種技術手段寫出儘可能少bug的程式,也會用同樣豐富的方法去debug,找出隱身的bug——那些產品中最凶險的敵人。
敏銳、機警、犀利的福爾摩斯能夠用他那一雙敏銳的眼睛加上兩隻靈敏的耳朵,覺察到別人忽略的東西,抓住一些細微的線索,利用精密的思想將一個個斷掉的連環重新接起,推理出事情發生的起因經過。程式設計師在debug的過程中,做的也是同樣的事情。這是一個能讓人倍受鼓舞的事,卻常讓悲觀主義者糾結:我哪有人家福爾摩斯的本事?上大學來,在程式設計的實踐中,就是來獲得這些本事的。不要將自己看成一個長成的葫蘆,再不能塞進上蒼饋贈的寶物。經歷與bug的鬥爭,可以學得福爾摩斯的本領。
程式設計師靠什麼發現bug出沒的蛛絲馬跡?除了大腦中的思維,觀察是必需的環節。看英文的編譯錯誤提示,是一種觀察,用於對付語法錯誤常常奏效。對付執行錯誤和邏輯錯誤,需要觀察程式的執行結果。執行結果不只是初學者做題目時常關注的最後執行結果,找bug要更關注程式執行過程裡中間結果的可疑之處。
一種簡單的方法就是在程式中,加入一些輸出語句,輸出值得懷疑的變數或表示式的值,再與自己的預期進行比較。例如,程式中執行了x=a/b,即將a除以b的運算結果賦值給x,但之後x參與的運算結果總是出錯,這時就可以在x=a/b之前加入一條語句,輸出a和b的值。為此,不同語言中需要加入不一樣的語句,見表3–1。
表3–1 各種語言輸出變數值的方法
語言 |
為了觀察加入的輸出a和b的值的語句 |
C |
printf("a和b的值:%d,%d\n", a, b); |
C++ |
cout<<"a和b的值:"<<a<<b<<endl; |
Java |
System.out.println("a和b的值:"+a+","+b); |
在執行程式中,會看到輸出的a和b的值。如果a和b的值是錯誤的,問題在之前;否則,安心在後面找bug。通過這樣一種觀察,不僅可以定位問題出現在什麼位置上,而且會啟發我們判斷出究竟是什麼錯誤。這是一種簡便的方法,也幾乎是萬能的辦法,很適合初學者。在實踐中,當沒有其他合適的工具供使用時,常常這樣做。但是,這種方法也增加了寫程式碼的工作量,除錯結束後,可能還得費事刪去各處不必要的輸出語句。有些程式設計師捨不得去掉這些“功臣”,給不必要的輸出加上註釋,或者用條件編譯等手段處理,卻使本來清晰的原始碼,看起來像乞丐的百衲衣。
神話故事中,神通廣大的人物,手中都是有些寶貝的,孫悟空有金箍棒,九尾狐手中有捆仙索。程式設計師除錯程式的寶物,就是除錯程式——一種用於幫助程式設計師除錯程式的程式。目前大多數的整合開發環境中,都提供了除錯程式。
教會讀者使用除錯程式已經超出本書範圍,但初窺這一寶物中的門道,還是有些必要的。讀者在實踐中正在用哪一個程式設計環境實踐,找到合適的手冊,或者找一個會用的人幫助帶一下,用一個程式體驗下來就能學會,這並不是一件太難的事。除錯程式是一種非常實用的工具,一朝學會,終身受益;早學會,早受益;花少許時間磨刀,砍柴又多又省力。這其中的道理,並不難理解。
圖3–2是在VC++6.0和CodeBlocks 10.05中除錯C/C++程式時,通過選單能夠觀察到的除錯功能,以此為例概述除錯(debug)過程中常用到的手段。
單步執行程式。讀程式時,我們常按計算機的“思維”在人腦中“執行”程式。在除錯程式時,常遇到的讓人鬱悶的情況是,明明知道程式應該執行這個分支,但感覺計算機偏偏走了另外一個分支;明明知道迴圈需要執行n多次,卻1次也不進去。這時我們可以“單步執行”,讓計算機不是按照它的脾氣一股腦地執行下去,而是在你的控制下,一步一步地往下執行,走哪條分支,迴圈、函式如何進入,如何退出,一目瞭然。單步執行的具體功能包括:
- Step Into:單步執行程式,遇到函式呼叫時,進入函式內部逐步執行(想著Into,進入到)。
- Step Over:單步執行程式,遇到函式呼叫時,並不進入函式內部,而是將其呼叫當作一個整體執行(想著over,跨過去)。
- Step Out:除錯程式時,結束某個正在執行的函式,返回到呼叫它的上一級函式,常用於知道當前執行的函式中不存在錯誤時返回(想著out,出去了)。
再看圖9–2中的選單,找到單步執行對應的幾個選項,再看選項前的圖示,是否已經給出了非常清晰的提示?你可以找一個儘可能簡單的程式,開始單步執行這個程式,這是一個經過體驗就可以做下去的事情,而不是僅盯著資料去看就能明白的。看到選單中每一個選項後面的快捷鍵了嗎?選選單是個麻煩的過程,日常的操作幾乎都是通過快捷鍵高效完成的。這些功能還可以在視窗中找到工具欄,上面也有對應的圖示,可以用滑鼠去點選。
在一個醒目的箭頭指示下,程式快活地在你的手中執行,一步,下一步,何去何從,一目瞭然。但,你是來找bug的,不是來看熱鬧的。在程式執行過程中,我們關心涉及到的資料是如何變化的。這是福爾摩斯斷案中需要發現的資訊,也是我們找到bug的依據。在單步執行過程中,為了找出問題所在,我們需要觀察變數和表示式的值的變化,並和事先的預期進行對比。
觀察變數和表示式值的變化。還是到圖9–2的選單上,我們分別看到了“QuickWatch...”和“Edit watches...”兩個選項,點選之後,將你關心的變數的名字輸入到後續的介面中去,在單步執行中,就會看到“觀察視窗”中變數值的變化。哪一個變數的值在執行中發生了變化,在執行的過程中,都能一覽無遺。這是一個很熱鬧的場景,展示的就是這個程式中的門道。觀察視窗中不僅可以展示即時的值,而且還可以觀察表示式的值,在單執行過程中,可以隨時新增。要是福爾摩斯再世,他也一定會羨慕我們的寶物了,將觀察視窗中展示的結果,和你頭腦中的預期相對照,bug就此無處藏身。
利用斷點。單步執行很給力,美中不足的是,要一步一步地執行,顯得效率低下。明明已經知道這一段迴圈沒有問題,但迴圈需要執行幾百次,點幾百次Step Over,這該讓人如何面對?
除錯程式中提供了“斷點”(BreakPoint)的功能。斷點是程式執行需要中斷的地方,由程式設計師自己設定。程式在執行過程中,會自動在斷點的位置停止下來,讓程式設計師觀察、分析。觀察完一個可疑點之後,可以讓程式自動執行到下一個斷點再進行觀察。成塊地跟蹤,理性地分析,這是一件很過癮的事情。
現在需要了解的功能就是:如何設定斷點(Toggle breakpoint或Insert breakpoint)、取消斷點(Remove breakpoint),如何執行到下一個斷點。你用的程式設計環境不同,方法會有些許差別,親自試一試吧。
執行到游標處。設定斷點需要費些心機,考慮好哪裡可疑,逐個標記好斷點,按部就班地進行處置。除錯程式還提供了“執行到游標”(Run to cursor)的功能,游標在哪裡,就執行到哪裡,所需要的,是VC++ 6.0中的Ctrl+F10或者是CodeBlocks中的F4,或者是別的鍵的組合,配合觀察視窗,好犀利。
工欲善其事,必先利其器。程式設計和軟體工程中,為了方便工作,有無數幫助專業人員提高工作效率和質量,降低工作強度的工具軟體。初學者中,不知道除錯工具的人不少,似乎現有的教材並不熱衷於介紹這些,而有些人知道卻稱“不習慣”而不用。如果不去用,是不會習慣的,也不會感受到其強大的功能,也不能享受找出bug帶來的快意。
寫規範的程式,不僅是好看一點
大學畢業生進了企業,編碼沒有規矩。——IT行業資深開發人員
實際上,編碼規範是一個老生常談的問題,幾乎在每家公司,都會採用一定的編碼規範要求開發團隊。例如C++程式設計中的規範,通過搜尋引擎,很容易找到谷歌、中興等中外公司的企業要求。有些人不喜歡編碼規範,因為這意味著可能不得不改變自己的程式設計習慣,有些人還認為編碼規範會破壞創造性和程式質量。在大學裡,教學的要求相對產品開發的要求寬鬆得多,老師會講到基本規範,但有些人拒不執行。無論怎麼講,按照編碼規範要求,寫出一手好程式,是一個應該從初學起就養成的好習慣。
編碼規範是提高程式可讀性、保證程式質量的重要指標。程式轉換為目的碼後是要由計算機執行的,但其質量需要人去保證,前提是能夠讓人容易地讀懂。有人不無誇張地說:“任何一個傻瓜都能寫出計算機可以理解的程式碼,唯有寫出人類容易理解的程式碼的程式設計師,才是優秀的程式設計師。”
編碼規範,幫助我們寫出人類容易理解的程式碼,例如識別符號的命名,適當的註釋,程式碼的縮排等,聽起來簡單,用上了價值非凡。不止一次,我在輔導學生上機時,學生面對莫名的問題折磨得就要崩潰了,用IDE中的“整理程式碼格式”的功能重新排版式,問題馬上一目瞭然,就是花括號不匹配而已。現在學程式設計的同學是幸福的,因為流行的IDE,整理程式碼格式的功能已經成為標配。有人放著寶物不去用,偏要生活在垃圾堆一般的程式碼裡。實際上,在IDE中錄入程式碼時,會很自然地寫出縮排有序的程式。只要習慣了,真的會覺得不規範的程式碼就是一堆垃圾。
編碼規範還有助於知識傳遞,提高程式設計師和學習者的個人能力。規範的程式碼由於其良好的可讀性,能讓人更迅速,更容易地理解,即使是陌生的程式碼,也能達到快速理解的目標。無論程式設計師,還是正在學習程式設計的大學生,沒有理由去抵制相關規則的存在,都應該養成良好的編碼習慣。在很多時候,一些風格良好的程式碼在默默地影響著技術風格的形成。這也要求學習者能從程式碼本身能看出功能之外的很多東西。在此也想譴責部分教科書,應該是想省點紙張的原因,將程式碼在排版時擠做一團:一邊在強調程式碼規範的重要性,一邊就在身先垂範著不規範的醜陋,以至於影響了初學者的編碼風格。
在實際的工作中,編碼規範能夠統一全域性,促進團隊協作,實際也能起到節約成本的目標。開發軟體是一個團隊活動,編碼規範要求團隊成員遵守統一的要求,包括在識別符號命名上的統一、註釋的方法,以及規範的開發流程,降低了出現缺陷的機會,減少了在維護階段的工作量,成員之間可以輕鬆地閱讀對方的程式碼,集中精力關注要完成的業務邏輯,這體現的都是效率和成本。
關於規範說了一堆,具體的規範還有待結合程式設計的實踐去落實。初學程式設計需要關注規範,但在有些人看來,似乎是一個可以暫時忽略的話題。然而一放再放只能使問題延後暴露,無形當中,是壞習慣與好習慣之間的博弈,是高效率或低效率、高產出或低產出的鬥爭。對初學者而言,適當的註釋、縮排排版、一句一行、規範的命名,這些是可以馬上做好的事。
返回到【全文目錄】