:順序結(jié)構(gòu)與分支結(jié)構(gòu)全解析)
1. 從一段“按部就班”的代碼說(shuō)起什么是順序結(jié)構(gòu)流程控制這個(gè)概念幾乎所有編程入門教材都會(huì)放在很靠前的位置講。原因很簡(jiǎn)單程序本質(zhì)上就是一條一條指令的執(zhí)行而流程控制決定這些指令按照什么順序、什么條件去執(zhí)行。就好比做一道菜先洗菜、再切菜、后下鍋這個(gè)先后次序就是順序結(jié)構(gòu)如果菜太咸了就加點(diǎn)水、太淡了就加點(diǎn)鹽這種“根據(jù)情況決定下一步做什么”的邏輯就是分支結(jié)構(gòu)。先看順序結(jié)構(gòu)。順序結(jié)構(gòu)是流程控制中最基礎(chǔ)、最直觀的一種代碼自上而下、逐行執(zhí)行每一條語(yǔ)句執(zhí)行完畢后才進(jìn)入下一條。寫過(guò)第一行“Hello World”的人其實(shí)已經(jīng)接觸過(guò)順序結(jié)構(gòu)只不過(guò)沒(méi)有意識(shí)到而已。它的特點(diǎn)是沒(méi)有任何跳躍、沒(méi)有判斷、沒(méi)有循環(huán)像一條沒(méi)有岔路的單行道。這里要特別注意一點(diǎn)很多初學(xué)者以為順序結(jié)構(gòu)“過(guò)于簡(jiǎn)單所以不重要”實(shí)際上恰恰相反。順序結(jié)構(gòu)是整個(gè)程序的地基。所有復(fù)雜的邏輯——無(wú)論是分支、循環(huán)還是函數(shù)調(diào)用——最終都落腳在順序執(zhí)行的基礎(chǔ)之上。程序執(zhí)行的入口是順序的初始化變量在使用前必須先聲明賦值這些“先來(lái)后到”的規(guī)則本質(zhì)上都是順序結(jié)構(gòu)的體現(xiàn)。我見(jiàn)過(guò)不少同學(xué)在剛接觸編程時(shí)代碼寫出來(lái)邏輯沒(méi)錯(cuò)可運(yùn)行結(jié)果總是不對(duì)。排查到最后往往是順序上出了問(wèn)題先使用了變量再賦值、先讀取了文件再打開(kāi)、先調(diào)用了一個(gè)依賴前置數(shù)據(jù)的函數(shù)卻把前置計(jì)算寫在后面。這些錯(cuò)誤說(shuō)白了就是順序結(jié)構(gòu)沒(méi)有真正理解到位。來(lái)看一個(gè)最簡(jiǎn)單的例子。以下是Python代碼# 先聲明變量并賦值 name 小明 age 18 # 依次輸出 print(姓名:, name) print(年齡:, age) print(明年年齡:, age 1)執(zhí)行順序完全是從上往下逐行執(zhí)行。第三行不會(huì)在第二行之前運(yùn)行第二行的age不會(huì)在賦值之前被讀取。這個(gè)過(guò)程不需要做任何判斷也不需要跳轉(zhuǎn)這就是順序結(jié)構(gòu)的本質(zhì)。如果你打算系統(tǒng)學(xué)習(xí)編程順序結(jié)構(gòu)是第一步而且值得你花時(shí)間寫幾個(gè)“按步驟執(zhí)行”的程序來(lái)加深體會(huì)。比如設(shè)計(jì)一個(gè)“輸入圓的半徑計(jì)算周長(zhǎng)和面積”的小程序整個(gè)過(guò)程就是讀取輸入、計(jì)算周長(zhǎng)、計(jì)算面積、輸出結(jié)果。每一步都依賴上一步產(chǎn)生的數(shù)據(jù)沒(méi)有任何選擇余地純粹的順序執(zhí)行。import math # 1. 輸入半徑 r float(input(請(qǐng)輸入圓的半徑: )) # 2. 計(jì)算周長(zhǎng)和面積 circumference 2 * math.pi * r area math.pi * r * r # 3. 輸出結(jié)果 print(周長(zhǎng):, round(circumference, 2)) print(面積:, round(area, 2))這個(gè)例子雖然簡(jiǎn)單但三步操作的先后順序至關(guān)重要你不可能在半徑還沒(méi)有輸入的時(shí)候就計(jì)算面積也不可能在計(jì)算面積之前就輸出結(jié)果。每一步都依賴上一步的輸出這種“步步銜接”的依賴關(guān)系正是順序結(jié)構(gòu)的核心價(jià)值所在。從更宏觀的角度看順序結(jié)構(gòu)也是最容易被忽略調(diào)試的存在。很多人遇到程序出錯(cuò)第一反應(yīng)是查邏輯、查算法卻忘了最基本的執(zhí)行順序。所以我建議初學(xué)者養(yǎng)成一個(gè)好習(xí)慣在代碼中按執(zhí)行順序標(biāo)注好序號(hào)就像上面例子里的注釋遇到程序行為不符合預(yù)期時(shí)先用“順序思維”檢查一遍——變量是否在讀取之前已經(jīng)賦值資源是否在使用之前已經(jīng)準(zhǔn)備好一個(gè)語(yǔ)句是否錯(cuò)誤地放在了另一個(gè)語(yǔ)句之前這些排查在大多數(shù)情況下能幫你省下大量時(shí)間。2. 走到岔路口分支結(jié)構(gòu)到底在解決什么問(wèn)題如果說(shuō)順序結(jié)構(gòu)是“沒(méi)有選擇的執(zhí)行”那么分支結(jié)構(gòu)就是“根據(jù)條件做出選擇的執(zhí)行”。它解決的核心問(wèn)題是當(dāng)程序面臨多個(gè)可能路徑時(shí)如何根據(jù)當(dāng)前的數(shù)據(jù)或狀態(tài)選擇其中一條路徑走下去。先問(wèn)一個(gè)問(wèn)題你寫一個(gè)“判斷成績(jī)是否及格”的程序用戶輸入一個(gè)分?jǐn)?shù)如果大于等于60分輸出“及格”否則輸出“不及格”。這個(gè)需求用純順序結(jié)構(gòu)能實(shí)現(xiàn)嗎答案是不能。因?yàn)槌绦驘o(wú)法同時(shí)輸出“及格”和“不及格”它必須在兩個(gè)結(jié)果之間選一個(gè)。這個(gè)“選擇”就是分支結(jié)構(gòu)要做的事。分支結(jié)構(gòu)在編程語(yǔ)言中主要通過(guò)if語(yǔ)句來(lái)實(shí)現(xiàn)部分語(yǔ)言還提供switch或match等更豐富的分支語(yǔ)法。它的核心邏輯是先計(jì)算一個(gè)條件表達(dá)式結(jié)果為真或假根據(jù)這個(gè)結(jié)果決定執(zhí)行哪一段代碼。用生活中的場(chǎng)景來(lái)做類比你出門前看天氣預(yù)報(bào)如果下雨就帶傘否則不帶。這個(gè)“如果下雨”就是條件帶不帶傘則對(duì)應(yīng)兩個(gè)分支。程序里的分支結(jié)構(gòu)就是把這個(gè)過(guò)程形式化、自動(dòng)化。用Python寫一個(gè)最簡(jiǎn)單的分支score int(input(請(qǐng)輸入成績(jī): )) if score 60: print(及格) else: print(不及格)這里的執(zhí)行流程很清晰程序先讀取用戶輸入的分?jǐn)?shù)然后執(zhí)行if score 60這個(gè)判斷。如果條件成立執(zhí)行print(及格)否則執(zhí)行print(不及格)。注意這里兩個(gè)分支只會(huì)執(zhí)行其中一個(gè)不會(huì)兩個(gè)都執(zhí)行也不會(huì)“看情況執(zhí)行一半”。這是分支結(jié)構(gòu)最重要的行為特征。需要注意的是不同語(yǔ)言的if語(yǔ)句寫法略有差異但底層的邏輯完全一致。C語(yǔ)言版的示例#include stdio.h int main() { int score; printf(請(qǐng)輸入成績(jī): ); scanf(%d, score); if (score 60) { printf(及格\n); } else { printf(不及格\n); } return 0; }不管是Python、C、Java還是JavaScriptif語(yǔ)句的核心結(jié)構(gòu)都是條件表達(dá)式 真分支 可選假分支。理解了這個(gè)通用邏輯切換到任何語(yǔ)言都只需要熟悉語(yǔ)法細(xì)節(jié)而已。再深入一步分支結(jié)構(gòu)不只有“兩選一”這一種形態(tài)。實(shí)際編程中經(jīng)常遇到多選一的情況成績(jī)A、B、C、D四個(gè)等級(jí)一天中的早中晚三個(gè)時(shí)段不同會(huì)員等級(jí)享受不同折扣。這時(shí)候就需要else ifPython中用elif或者switch結(jié)構(gòu)來(lái)實(shí)現(xiàn)了。score int(input(請(qǐng)輸入成績(jī): )) if score 90: grade A elif score 80: grade B elif score 70: grade C elif score 60: grade D else: grade F print(等級(jí):, grade)這段代碼的執(zhí)行邏輯是一個(gè)“漏斗式判定”從上到下依次判斷只要某個(gè)條件成立后面的所有條件都不再執(zhí)行。這里有一個(gè)非常關(guān)鍵的細(xì)節(jié)條件的順序會(huì)影響結(jié)果。如果你把score 60寫在score 90前面那么輸入95分時(shí)會(huì)先滿足score 60直接判定為D后面的判斷根本不會(huì)執(zhí)行。這就是一個(gè)經(jīng)典的分支順序陷阱。所以寫多分支判斷時(shí)有一條經(jīng)驗(yàn)法則范圍越嚴(yán)格的、條件越苛刻的放在越前面。先判斷90分以上的情況再判斷80分以上的逐級(jí)往下降這樣每個(gè)檔位才能準(zhǔn)確命中。3. 分支結(jié)構(gòu)的高級(jí)形態(tài)嵌套分支與switch結(jié)構(gòu)當(dāng)分支里面還套著分支時(shí)就形成了嵌套分支。繼續(xù)以成績(jī)判斷為例如果一個(gè)學(xué)生成績(jī)及格我們還想進(jìn)一步判斷是優(yōu)秀還是良好就需要在if分支內(nèi)部再寫一個(gè)if。score int(input(請(qǐng)輸入成績(jī): )) if score 60: if score 90: print(優(yōu)秀) elif score 80: print(良好) else: print(及格) else: print(不及格)嵌套分支適合處理“主條件決定是否進(jìn)入下一層判斷”的場(chǎng)景。生活中也有類似的例子你決定是否出門先看天氣如果下雨再判斷雨大不大決定是步行還是打車。不過(guò)嵌套分支有一個(gè)明顯的缺點(diǎn)層級(jí)太深時(shí)代碼可讀性會(huì)急劇下降。一段超過(guò)三層嵌套的if-else會(huì)讓人讀起來(lái)頭大調(diào)試時(shí)也容易出問(wèn)題。后面我會(huì)專門說(shuō)這一點(diǎn)。再來(lái)看另一種多分支結(jié)構(gòu)switch。很多學(xué)習(xí)者困惑“什么時(shí)候用else if什么時(shí)候用switch”。簡(jiǎn)單說(shuō)switch適合條件是固定值匹配的場(chǎng)景而else if適合范圍判斷或復(fù)雜表達(dá)式判斷的場(chǎng)景。以C語(yǔ)言為例#include stdio.h int main() { int weekday; printf(請(qǐng)輸入星期1-7: ); scanf(%d, weekday); switch (weekday) { case 1: printf(周一\n); break; case 2: printf(周二\n); break; case 3: printf(周三\n); break; case 4: printf(周四\n); break; case 5: printf(周五\n); break; case 6: case 7: printf(周末\n); break; default: printf(輸入無(wú)效\n); break; } return 0; }這段代碼有幾個(gè)值得注意的要點(diǎn)。第一case后面的值必須是常量不能是變量或表達(dá)式。第二每個(gè)分支末尾的break非常關(guān)鍵——如果漏掉break程序會(huì)繼續(xù)往下執(zhí)行下一個(gè)case的代碼這種現(xiàn)象叫“穿透”。上面case 6和case 7故意連續(xù)排列利用的就是穿透特性無(wú)論輸入6還是7都會(huì)合并執(zhí)行同一個(gè)代碼塊。這種寫法是故意的屬于合理運(yùn)用。switch和if-else if的取舍以我的實(shí)際經(jīng)驗(yàn)來(lái)看判斷條件是“整型擴(kuò)列、字符或枚舉常量”這類有限集合時(shí)優(yōu)先選switch因?yàn)榻Y(jié)構(gòu)清晰、可讀性好判斷條件是“范圍”如成績(jī)、年齡區(qū)間或“復(fù)雜布爾邏輯”時(shí)只能選if-else if。這不是性能問(wèn)題純粹是表達(dá)能力和代碼可維護(hù)性的考量。這里額外提醒一條有些語(yǔ)言比如Python沒(méi)有原生switch不過(guò)Python 3.10之后加入了match語(yǔ)句語(yǔ)法形態(tài)不同但思路相近。如果未來(lái)你在Python里看到match知道它和switch是一類東西就行了。4. 順序與分支的配合一個(gè)完整的真實(shí)場(chǎng)景講完了兩種基礎(chǔ)結(jié)構(gòu)自然要問(wèn)一個(gè)問(wèn)題它們是怎么配合的其實(shí)幾乎所有的業(yè)務(wù)程序都是“順序執(zhí)行的大框架下局部插入分支判斷”的形態(tài)。順序結(jié)構(gòu)負(fù)責(zé)“按步驟推進(jìn)”分支結(jié)構(gòu)負(fù)責(zé)“在關(guān)鍵節(jié)點(diǎn)做出決策”。舉個(gè)例子。假設(shè)你要設(shè)計(jì)一個(gè)“快遞運(yùn)費(fèi)計(jì)算器”規(guī)則如下首重1公斤內(nèi)10元超出部分每公斤加8元如果寄件地址屬于偏遠(yuǎn)地區(qū)額外加收15元偏遠(yuǎn)費(fèi)如果包裹重量超過(guò)20公斤拒收。這個(gè)需求就是順序結(jié)構(gòu)和分支結(jié)構(gòu)的典型配合。實(shí)現(xiàn)步驟拆解如下順序執(zhí)行讀取重量、讀取是否偏遠(yuǎn)地區(qū)分支判斷一重量是否超過(guò)20公斤是則拒絕分支判斷二是否偏遠(yuǎn)地區(qū)是則加偏遠(yuǎn)費(fèi)分支判斷三重量是否超過(guò)首重判斷是否加續(xù)重費(fèi)順序執(zhí)行匯總輸出運(yùn)費(fèi)來(lái)看代碼weight float(input(請(qǐng)輸入包裹重量公斤: )) remote input(是否為偏遠(yuǎn)地區(qū)(y/n): ) y # 先判斷重量超限直接拒絕 if weight 20: print(無(wú)法寄送超重) else: base_price 10 # 首重費(fèi)用 # 計(jì)算續(xù)重費(fèi)用 if weight 1: extra_weight weight - 1 extra_price extra_weight * 8 else: extra_price 0 total_price base_price extra_price # 偏遠(yuǎn)地區(qū)加價(jià) if remote: total_price 15 print(運(yùn)費(fèi):, total_price, 元)這個(gè)場(chǎng)景完美展示了兩種結(jié)構(gòu)的協(xié)同方式外層是一個(gè)大的順序流程輸入→判斷→計(jì)算→輸出每個(gè)環(huán)節(jié)內(nèi)部再用分支去處理不同的情況。程序在執(zhí)行過(guò)程中遇到分支時(shí)“偏離主線走一段”分支結(jié)束后又回到主線繼續(xù)向下走。對(duì)這種配合方式初學(xué)者需要建立一種“模塊化讀代碼”的思維方式。看到一段完整代碼時(shí)不要試圖從頭到尾“背誦”它的執(zhí)行軌跡而是先識(shí)別出哪些部分是順序執(zhí)行的框架哪些部分是分支決策的節(jié)點(diǎn)把代碼拆解成“主干”和“分叉”兩部分。這樣無(wú)論是閱讀還是調(diào)試效率都會(huì)高很多。5. 條件表達(dá)式解析分支判斷背后的“真與假”分支結(jié)構(gòu)的核心是條件表達(dá)式。條件表達(dá)式的真假?zèng)Q定程序走哪個(gè)分支所以理解“什么情況下為真、什么情況下為假”極其重要。先看布爾值boolean的概念。布爾類型只有兩個(gè)值True和False在不同語(yǔ)言中可能是true/false、1/0。一切條件表達(dá)式的結(jié)果最終都會(huì)被歸結(jié)為布爾值。在大多數(shù)語(yǔ)言中0被視為假、非0視為真在Python中None、0、空字符串、空列表[]等“空值”都被視為假其他值被視為真。這是個(gè)很重要的差異點(diǎn)后面細(xì)講。常見(jiàn)的條件表達(dá)式按功能可以分為以下幾類比較運(yùn)算,,,,,!用于比較兩個(gè)值的大小或相等性。注意一個(gè)高頻坑判斷相等必須用而單個(gè)是賦值。初學(xué)者經(jīng)常把if score 60寫成賦值語(yǔ)句這在有些語(yǔ)言里能編譯通過(guò)但結(jié)果完全錯(cuò)誤條件恒為真調(diào)試起來(lái)非常隱蔽。邏輯運(yùn)算and并且、or或者、not取反用于組合多個(gè)條件。一個(gè)典型場(chǎng)景判斷一個(gè)年份是否為閏年——能被4整除但不能被100整除或者能被400整除。year int(input(請(qǐng)輸入年份: )) if (year % 4 0 and year % 100 ! 0) or year % 400 0: print(year, 是閏年) else: print(year, 不是閏年)這個(gè)閏年判斷是邏輯運(yùn)算的經(jīng)典練習(xí)。拆開(kāi)理解能被4整除且不能被100整除這是普通閏年的規(guī)則能被400整除這是世紀(jì)閏年的特殊規(guī)則。兩者之間是“或”的關(guān)系。寫這種復(fù)合條件時(shí)建議用括號(hào)把邏輯分組表達(dá)清楚避免因?yàn)檫\(yùn)算符優(yōu)先級(jí)問(wèn)題導(dǎo)致判斷失誤。成員判斷在Python里可以用in判斷某個(gè)元素是否在列表或字符串中。比如判斷一個(gè)字符是否是元音字母。char input(請(qǐng)輸入一個(gè)字母: ).lower() vowels aeiou if char in vowels: print(char, 是元音字母) else: print(char, 不是元音字母)這種寫法比一連串的or要簡(jiǎn)潔得多也是Python風(fēng)格推薦的寫法。條件表達(dá)式的陷阱也不少。以我在教學(xué)中反復(fù)強(qiáng)調(diào)的一個(gè)為例浮點(diǎn)數(shù)比較。0.1 0.2 0.3在大多數(shù)編程語(yǔ)言中的結(jié)果是False因?yàn)楦↑c(diǎn)數(shù)在內(nèi)存中無(wú)法精確表示計(jì)算會(huì)產(chǎn)生微小的誤差。處理這類問(wèn)題時(shí)不要直接比較兩個(gè)浮點(diǎn)數(shù)是否相等而是比較它們的差值是否小于一個(gè)足夠小的閾值a 0.1 0.2 b 0.3 epsilon 0.0000001 if abs(a - b) epsilon: print(相等) else: print(不相等)寫分支結(jié)構(gòu)時(shí)一定要意識(shí)到條件表達(dá)式不只是“寫對(duì)了邏輯就萬(wàn)事大吉”還牽扯到數(shù)據(jù)類型、邊界情況、精度問(wèn)題等細(xì)節(jié)。這也是分支結(jié)構(gòu)練習(xí)比順序結(jié)構(gòu)容易出錯(cuò)的地方。6. 常見(jiàn)錯(cuò)誤與排查技巧實(shí)錄分支結(jié)構(gòu)相關(guān)的報(bào)錯(cuò)和邏輯錯(cuò)誤你在練習(xí)頭歌實(shí)驗(yàn)四或日常刷題時(shí)一定會(huì)遇到。我把最常見(jiàn)的幾類整理成一個(gè)速查表方便你對(duì)照排查。錯(cuò)誤類型具體表現(xiàn)原因解決方案賦值代替相等判斷if a 5:把寫成了統(tǒng)一記住判斷相等雙等號(hào)條件順序不當(dāng)輸入95分得到D等級(jí)寬泛條件寫在了嚴(yán)格條件之前遵循“嚴(yán)格條件在前”原則漏掉breakswitch執(zhí)行了多個(gè)分支沒(méi)有寫break造成穿透每個(gè)case末尾補(bǔ)break故意穿透除外嵌套過(guò)深代碼縮進(jìn)錯(cuò)亂、邏輯混亂分支套分支層數(shù)過(guò)多重構(gòu)邏輯或提取為函數(shù)邊界值處理錯(cuò)誤判斷及格但60分被歸為不及格忽略了和的區(qū)別明確邊界值的歸屬使用紙筆推演邏輯運(yùn)算優(yōu)先級(jí)a or b and c結(jié)果與預(yù)期不符不清楚and優(yōu)先于or用括號(hào)顯式表達(dá)優(yōu)先級(jí)分支體為空某些條件下什么都不執(zhí)行if分支內(nèi)忘記寫語(yǔ)句先確認(rèn)是否真的需要空邏輯必要時(shí)用pass占位在上面這些常見(jiàn)錯(cuò)誤中我想特別展開(kāi)說(shuō)兩個(gè)。第一個(gè)是“邊界值理解不到位”。對(duì)于if score 60這種判斷邊界值就是60。它到底判為及格還是不及格取決于你用的是還是。這種模棱兩可的問(wèn)題在設(shè)計(jì)程序時(shí)就應(yīng)該明確而不是等到測(cè)試用例跑出來(lái)再猜。建議每次寫完一段判斷邏輯都手動(dòng)測(cè)試一遍邊界值等于邊界、略小于邊界、略大于邊界三種情況都跑一遍確認(rèn)行為符合預(yù)期。第二個(gè)是“嵌套層級(jí)過(guò)深導(dǎo)致的可讀性災(zāi)難”。三層的if-else嵌套已經(jīng)是多數(shù)人閱讀理解能力的極限。超過(guò)三層代碼就會(huì)變得很難維護(hù)。以一個(gè)用戶登錄邏輯為例先判斷用戶名是否存在再判斷密碼是否正確再判斷賬號(hào)是否被鎖定——這三層嵌套勉強(qiáng)能接受如果再加上“判斷驗(yàn)證碼是否正確”這第四層讀起來(lái)就非常吃力了。針對(duì)這個(gè)問(wèn)題業(yè)界常用“提前返回”來(lái)降低嵌套深度。用偽代碼說(shuō)明def login(username, password): if not user_exists(username): return 用戶不存在 if not check_password(username, password): return 密碼錯(cuò)誤 if is_locked(username): return 賬號(hào)已鎖定 return 登錄成功這個(gè)寫法把多層嵌套扁平化成了一次性順序判斷邏輯清晰一目了然。這就是很多進(jìn)階經(jīng)驗(yàn)中提到的“用早期return替代嵌套if”的設(shè)計(jì)思路在處理分支結(jié)構(gòu)時(shí)非常好用。7. 分支結(jié)構(gòu)練習(xí)的進(jìn)階思路如果你正在做頭歌實(shí)驗(yàn)四的分支結(jié)構(gòu)練習(xí)或者想在課后繼續(xù)鞏固可以參考下面這套練習(xí)思路。它比我當(dāng)年苦練分支語(yǔ)法時(shí)系統(tǒng)得多。第一步先把基礎(chǔ)語(yǔ)法過(guò)一遍。無(wú)論你用的是Python、C還是Java至少能手寫以下能力的代碼單分支if、雙分支if-else、多分支elif、嵌套分支、switch如果有的話。這一步的目標(biāo)不是“看懂”而是“不看參考能默寫”。第二步用題目來(lái)加深理解。推薦練習(xí)以下經(jīng)典題型輸入三個(gè)數(shù)按從小到大排序輸出輸入年份判斷是否閏年輸入成績(jī)輸出等級(jí)A/B/C/D/F輸入三角形三邊長(zhǎng)度判斷是否能構(gòu)成三角形以及是等邊、等腰還是普通三角形輸入一個(gè)字符判斷是數(shù)字、大寫字母、小寫字母還是其他字符這些題目覆蓋了比較運(yùn)算、邏輯運(yùn)算、嵌套分支、邊界值處理等幾乎全部分支結(jié)構(gòu)的核心場(chǎng)景。每一道題都能完全用分支結(jié)構(gòu)解決還沒(méi)有引入循環(huán)非常適合集中鍛煉。第三步做“條件組合”的專項(xiàng)練習(xí)。這是很多人的盲區(qū)——單條件判斷沒(méi)問(wèn)題一旦條件變成“且”“或”的組合就亂套??梢宰鲱愃七@樣的練習(xí)某網(wǎng)站會(huì)員等級(jí)規(guī)則——普通會(huì)員不打折銀卡會(huì)員95折金卡會(huì)員9折鉆石會(huì)員8折同時(shí)單筆消費(fèi)滿1000元的用戶額外享受立減50元優(yōu)惠。請(qǐng)你寫出這段折扣邏輯。這類題目需要綜合運(yùn)用多分支和復(fù)合條件是鍛煉分支思維的好材料。第四步挑戰(zhàn)“分支結(jié)構(gòu) 函數(shù)封裝”的組合練習(xí)。嘗試把一段復(fù)雜的分支邏輯封裝成函數(shù)讓主程序更簡(jiǎn)潔。例如把上面的運(yùn)費(fèi)計(jì)算邏輯封裝成calculate_freight(weight, remote)函數(shù)在函數(shù)內(nèi)部用分支結(jié)構(gòu)處理各種情況在主程序中只需要調(diào)用函數(shù)即可。這個(gè)層次已經(jīng)不是單純的語(yǔ)法練習(xí)而是開(kāi)始接觸“模塊化思維”了。整個(gè)練習(xí)過(guò)程我一直建議學(xué)生動(dòng)手畫“流程圖”。別覺(jué)得畫圖麻煩一張A4紙把分支的走向畫清楚比盯著一堆代碼空想要高效得多。流程圖能把邏輯可視化很多讀代碼時(shí)注意不到的邊界問(wèn)題在畫圖時(shí)會(huì)被迫暴露出來(lái)。8. 順序結(jié)構(gòu)和分支結(jié)構(gòu)的關(guān)系從“讀寫代碼”到“設(shè)計(jì)程序”很多人學(xué)完順序結(jié)構(gòu)和分支結(jié)構(gòu)能做題、能通過(guò)頭歌平臺(tái)的實(shí)驗(yàn)但遇到一個(gè)全新的需求時(shí)還是不知道從哪下手。這個(gè)問(wèn)題的根源在于知識(shí)學(xué)會(huì)了但設(shè)計(jì)能力沒(méi)有跟上。從“會(huì)寫代碼”到“會(huì)設(shè)計(jì)程序”中間需要跨過(guò)一個(gè)門檻。順序和分支只是工具真正重要的是如何使用這些工具去拆解一個(gè)復(fù)雜的實(shí)際問(wèn)題。我自己帶新人的時(shí)候常說(shuō)一句話拿到需求先不要急著開(kāi)編輯器先拿出一張紙把整個(gè)流程“翻譯”成人話。比如你要做一個(gè)“商場(chǎng)促銷結(jié)算程序”流程可以拆成輸入顧客購(gòu)買的商品金額輸入顧客的會(huì)員等級(jí)根據(jù)會(huì)員等級(jí)算出折扣價(jià)判斷是否滿減計(jì)算最終價(jià)格輸出最終應(yīng)付金額這個(gè)過(guò)程中第1、2、5步是順序結(jié)構(gòu)第3、4步是分支結(jié)構(gòu)。你可以先在紙上畫出流程圖用矩形框表示“順序執(zhí)行”的步驟用菱形框表示“條件判斷”的分支節(jié)點(diǎn)再用箭頭連接起來(lái)。畫完之后你會(huì)發(fā)現(xiàn)代碼結(jié)構(gòu)已經(jīng)基本浮現(xiàn)出來(lái)了一個(gè)主流程加上兩個(gè)判斷節(jié)點(diǎn)每個(gè)判斷節(jié)點(diǎn)內(nèi)可能還有子流程。這種設(shè)計(jì)方法有一個(gè)專業(yè)術(shù)語(yǔ)叫“自頂向下逐步細(xì)化”。它強(qiáng)調(diào)從整體到局部的設(shè)計(jì)思路先把大流程梳理清楚再逐個(gè)細(xì)化復(fù)雜節(jié)點(diǎn)。無(wú)論用哪種語(yǔ)言、寫多大的系統(tǒng)這套方法論基本貫穿始終。下一個(gè)常見(jiàn)問(wèn)題是順序和分支學(xué)會(huì)了循環(huán)還沒(méi)學(xué)能做什么項(xiàng)目答案是很多。比如“簡(jiǎn)易計(jì)算器”輸入兩個(gè)數(shù)和運(yùn)算符輸出結(jié)果、“BMI指數(shù)判斷”輸入身高體重輸出偏瘦、正常、偏胖、“星座查詢”輸入生日輸出星座及性格描述等等。這些項(xiàng)目不需要復(fù)雜的語(yǔ)法一套分支結(jié)構(gòu)足以實(shí)現(xiàn)做出來(lái)也很有成就感。個(gè)人認(rèn)為學(xué)流程控制最忌諱的就是“一直做練習(xí)題從不做小項(xiàng)目”。練習(xí)題幫助你掌握語(yǔ)法點(diǎn)但小項(xiàng)目才能幫助你建立“需求→設(shè)計(jì)→編碼→測(cè)試”的完整閉環(huán)。哪怕是一個(gè)五十行的計(jì)算器程序走完整個(gè)閉環(huán)帶來(lái)的收獲遠(yuǎn)大于刷十道判斷題。9. 我的實(shí)操心得與幾點(diǎn)補(bǔ)充寫了這么多最后分享幾條我在實(shí)際教學(xué)和編碼中積累的經(jīng)驗(yàn)。關(guān)于順序結(jié)構(gòu)最大的心得是“初始化習(xí)慣”。所有變量在使用前都應(yīng)該有明確的初始值這是順序結(jié)構(gòu)的基本要求。我見(jiàn)過(guò)太多“變量未初始化就參與運(yùn)算”導(dǎo)致的詭異錯(cuò)誤尤其在C語(yǔ)言中局部變量不初始化時(shí)值是隨機(jī)的可能每次運(yùn)行結(jié)果都不一樣。Python雖然省去了顯式聲明類型但“數(shù)據(jù)必須先到位再用”這個(gè)原則依然適用。關(guān)于分支結(jié)構(gòu)最大的心得是“邊界值和異常輸入優(yōu)先”。很多分支判斷的錯(cuò)誤源于設(shè)計(jì)者只考慮了“正常情況”而忽略了邊界和異常。比如判斷成績(jī)等級(jí)只測(cè)試了85、72這樣的分?jǐn)?shù)卻漏了0分、100分、-5分、120分。寫分支代碼時(shí)建議形成自己的固定測(cè)試清單正常值、邊界值、非法值。每次寫完代碼至少把這三類值都測(cè)一遍能在很大程度上減少隱藏bug。關(guān)于調(diào)試方法推薦一個(gè)很實(shí)用的技巧打點(diǎn)輸出法。在分支判斷的關(guān)鍵節(jié)點(diǎn)插入臨時(shí)的輸出語(yǔ)句打印出當(dāng)前變量的值和流程走到哪一步了。比如score int(input(請(qǐng)輸入成績(jī): )) print(拿到分?jǐn)?shù):, score) if score 60: print(走了及格分支) else: print(走了不及格分支)確認(rèn)邏輯無(wú)誤后再刪掉這些調(diào)試輸出。相比使用高級(jí)調(diào)試器逐行跟蹤打點(diǎn)輸出法簡(jiǎn)單直接對(duì)初學(xué)者尤其友好。它能把“復(fù)雜的執(zhí)行過(guò)程”變成“可觀察的步驟記錄”極大減少憑猜測(cè)調(diào)試的挫敗感。最后補(bǔ)充一點(diǎn)與后續(xù)學(xué)習(xí)方向的銜接。順序結(jié)構(gòu)、分支結(jié)構(gòu)、循環(huán)結(jié)構(gòu)通常并稱“三大基本結(jié)構(gòu)”掌握了這三個(gè)你就拿到了閱讀世界上絕大多數(shù)程序邏輯的鑰匙。循環(huán)可以理解為“重復(fù)執(zhí)行的順序或分支”函數(shù)可以理解為“封裝好的順序、分支、循環(huán)組合體”對(duì)象與類則可以理解為“用更高級(jí)的方式組織這些結(jié)構(gòu)”。所以現(xiàn)在打好流程控制的地基后續(xù)學(xué)循環(huán)、函數(shù)、類時(shí)都會(huì)輕松很多。如果你正在做頭歌實(shí)驗(yàn)四或類似的流程控制練習(xí)先不要急于搜答案。用我前面提到的方法——先畫流程圖、再寫代碼、最后測(cè)試邊界值——把每一步都走扎實(shí)。流程控制學(xué)得好不好直接決定了你后面寫復(fù)雜程序時(shí)的底氣和信心。