據(jù)可視化與腳本調(diào)試)
1. 環(huán)境與工作區(qū)先把桌面看明白再動手很多人學(xué)MATLAB第一件事就是打開軟件然后被一堆面板困住光標(biāo)閃半天不知道往哪敲。我一開始也這樣后來才發(fā)現(xiàn)MATLAB的界面布局其實是為“從輸入到輸出”這條鏈路設(shè)計的認(rèn)清每個區(qū)域的作用比多背幾十條命令更有用。剛裝好的MATLAB默認(rèn)會展示幾個核心區(qū)域。命令窗口Command Window是最先映入眼簾的這里是整個軟件的心臟所有直接執(zhí)行的代碼都在這里敲。工作區(qū)Workspace在側(cè)面記錄著當(dāng)前會話里創(chuàng)建的所有變量雙擊變量還能直接在變量編輯器中打開查看細(xì)節(jié)。當(dāng)前文件夾Current Folder則負(fù)責(zé)顯示你正在工作的目錄腳本文件、數(shù)據(jù)文件都從這里讀寫。還有一個容易被忽略但很關(guān)鍵的“路徑管理”功能——MATLAB只能調(diào)用位于當(dāng)前目錄或搜索路徑下的函數(shù)和腳本所以如果你自己寫的.m文件不在工作目錄里命令行會直接報錯說找不到文件。剛開始練習(xí)時我建議把界面拆成“手動敲代碼”和“長期維護(hù)”兩個場景。臨時驗證一行命令就放在命令窗口寫成正式腳本放到編輯器Editor里。編輯器里的代碼可以逐節(jié)運(yùn)行還能下斷點(diǎn)調(diào)試這是后面寫復(fù)雜程序時最常用的工具。要打開編輯器并新建腳本直接用快捷鍵CtrlN這個習(xí)慣越早養(yǎng)成越好。相比直接在命令行里堆命令腳本文件具備可追溯性——過了三天再翻回來你還能看明白當(dāng)時每一步在做什么。還有一個細(xì)節(jié)工作區(qū)的變量會在關(guān)閉軟件后全部消失。如果你算到一半想回家繼續(xù)一定要用save命令把數(shù)據(jù)存到.mat文件里下次用load讀回來。不過這一點(diǎn)后面會專門講這里先記住“工作區(qū)是臨時的文件才是長久的”這個原則就行。環(huán)境的問題理清楚之后從數(shù)字到矩陣從字符串到邏輯類型你會發(fā)現(xiàn)MATLAB的數(shù)據(jù)世界其實有一套非常自洽的規(guī)則。2. 變量與數(shù)據(jù)類型從一段最簡單的加法說起打開命令窗口輸入a 3回車。命令窗口會返回一行“a 3”。如果你不想每次都被這個回顯刷屏就在命令結(jié)尾加上分號a 3;回車后什么也不顯示但工作區(qū)里已經(jīng)多了一個變量。分號這個東西是新手最容易忽略卻最實用的符號。在腳本中寫代碼時每個中間結(jié)果如果都回顯出來不僅刷屏運(yùn)行速度也會受拖累。所以行業(yè)內(nèi)有個不成文的習(xí)慣中間計算全部加分號屏蔽只有最后需要看的結(jié)果才不加分號。這個習(xí)慣從第一天就要養(yǎng)成否則后面寫循環(huán)和函數(shù)時會很難受。變量名的規(guī)則也需要留意。MATLAB的變量名區(qū)分大小寫A和a是兩個完全不同的變量。不能以數(shù)字開頭不能包含中文和空格不建議使用內(nèi)置函數(shù)名比如mean、sum這類作為變量名否則會覆蓋掉函數(shù)原本的功能后面再調(diào)的時候就報錯。這些規(guī)則與實際工程中的代碼規(guī)范是一致的——變量名要能看出含義比如用temperature而不是t1這種天書式命名。數(shù)據(jù)類型方面MATLAB默認(rèn)的核心類型是double雙精度浮點(diǎn)數(shù)整型、單精度、字符數(shù)組這些都需要顯式轉(zhuǎn)換。但這不意味著你要處處關(guān)心類型MATLAB的自動轉(zhuǎn)換機(jī)制已經(jīng)很成熟絕大多數(shù)場景下你直接賦值的數(shù)字就是double。只有遇到精度要求極其苛刻或內(nèi)存受約束的大規(guī)模運(yùn)算時才需要主動考慮數(shù)據(jù)類型。字符串與字符數(shù)組也是繞不開的話題。MATLAB早期只有char數(shù)組用單引號包裹比如name AppData它是按字符逐一存儲的數(shù)組。后來引入了string類型用雙引號包裹它把整個字符串當(dāng)作獨(dú)立對象處理支持更多方便的字符串函數(shù)。初學(xué)者很容易這兩者混用實際上很多API還會自動兼容兩者但如果你要做字符串拼接、分割、查找替換這些操作建議優(yōu)先使用雙引號的string類型代碼會更直觀。數(shù)字類型、字符串類型、邏輯類型true/false這三類覆蓋了你90%的入門期用法先吃透它們比強(qiáng)行研究表格、結(jié)構(gòu)體、元胞數(shù)組要務(wù)實得多。復(fù)雜數(shù)據(jù)類型放到后面配合真問題來學(xué)效果更好。3. 數(shù)組與矩陣操作MATLAB真正的主場如果只允許選一個理由來解釋“為什么大家都在用MATLAB”我會選“矩陣操作”。MATLAB的全稱本來就是Matrix Laboratory——矩陣實驗室它在數(shù)組上的表達(dá)能力幾乎比任何主流編程語言都打得更順。3.1 創(chuàng)建數(shù)組的三種基本姿勢手動列舉也叫直接輸入法。小數(shù)組可以直接手敲例如v [1 2 3 4 5]; M [1 2 3; 4 5 6; 7 8 9];方括號里用空格或者逗號隔開的是同一行的元素用分號隔開的是換行。所以上面的M就是一個3行3列的矩陣。這是最直觀的創(chuàng)建方式適合小規(guī)模數(shù)據(jù)的快速測試。冒號生成法是MATLAB的招牌語法。一維數(shù)組也可以用冒號快速生成比如t 0:0.1:10; % 從0到10每步0.1冒號的兩端分別是起始值、步長、終止值。如果省略步長默認(rèn)為1。這個寫法在信號處理領(lǐng)域極其常用比如生成時間軸來畫波形圖或者遍歷迭代。但要提醒一點(diǎn)浮點(diǎn)步長有累積誤差的可能性如果你的數(shù)組要精確到小數(shù)點(diǎn)后三年建議用linspace(0, 10, 101)這樣的線性間隔生成函數(shù)它在內(nèi)部會做更好的誤差控制。函數(shù)生成法比如零矩陣、單位陣、隨機(jī)矩陣。zeros(3,4)生成3行4列的全零矩陣eye(5)生成5階單位陣rand(2,3)生成0到1之間均勻分布的隨機(jī)矩陣。這些函數(shù)在后面線性代數(shù)計算、初始化存儲空間、生成測試數(shù)據(jù)時都是極高頻的調(diào)用。3.2 索引、切片與維度操作數(shù)組建好后接下來要處理的問題就是“怎么把特定的行、列、子塊取出來”。MATLAB的索引從1開始這和Python等語言完全不同。新手遷移到MATLAB時最容易犯的錯誤就是按0索引去取數(shù)據(jù)結(jié)果一直報“索引超出數(shù)組范圍”?;A(chǔ)索引寫法是M(2,3)表示第2行第3列。你想取單獨(dú)一行用M(2,:)冒號代表全部取單獨(dú)一列則是M(:,3)。這種語法邏輯非常符合人類的表達(dá)直覺——第幾行第幾列一行寫下沒有任何括號套括號的負(fù)擔(dān)。維度操作方面size(M)返回矩陣的行數(shù)和列數(shù)length(v)返回數(shù)組長度reshape(M, 2, 6)可以把一個矩陣按列的順序重排成另一個維度。還有一個非常常用的是transpose也就是轉(zhuǎn)置在MATLAB里用撇號M.表示復(fù)數(shù)矩陣要加M做共軛轉(zhuǎn)置。這兩者的區(qū)別在實數(shù)域里看不出來到復(fù)數(shù)信號處理時就是天壤之別一定要記得區(qū)分。3.3 矩陣運(yùn)算與數(shù)組運(yùn)算的坑矩陣乘法和對應(yīng)元素乘法這是初學(xué)者最容易搞混的地方。A * B做的是線性代數(shù)中的矩陣乘法內(nèi)積規(guī)則要求A的列數(shù)必須等于B的行數(shù)而A .* B是讓A和B對應(yīng)位置的元素逐個相乘要求兩個矩陣的維度必須完全一致。我見過太多人因為在寫濾波算法時少打了一個點(diǎn)結(jié)果整個輸出完全不是預(yù)期。這不是心態(tài)問題而是MATLAB語法本身就刻意區(qū)分了這兩種語義——前者是數(shù)學(xué)上嚴(yán)格的矩陣乘法后者更接近C語言或Python里的數(shù)組逐元素運(yùn)算。按照MATLAB的設(shè)計哲學(xué)矩陣的默認(rèn)操作是線性代數(shù)操作需要逐元素語義時才使用“點(diǎn)運(yùn)算符”。初學(xué)者只要記住一點(diǎn)如果你想做“對應(yīng)位置相乘”無論如何都要在符號前加上點(diǎn)除了加法和減法乘法和除法都需要注意這個點(diǎn)。另一個坑是廣播機(jī)制。Python的NumPy有自動廣播broadcasting但MATLAB在早期版本里對維度不對齊的數(shù)組運(yùn)算非常嚴(yán)格一個3x1的數(shù)組和一個1x3的數(shù)組做逐元素乘法是會直接報錯的。新版MATLAB引入了部分兼容行為但我仍然建議你養(yǎng)成“手動擴(kuò)展維度”的習(xí)慣接觸比較多的工具是bsxfun老版本和隱式擴(kuò)展新版本直接支持。實際做工程時顯式寫清楚維度的代碼往往調(diào)試起來更省心。4. 腳本編寫與調(diào)試從“敲命令”升級為“寫程序”你在命令行里能做的事幾乎都可以寫進(jìn)腳本文件里把輸入條件換一換一次運(yùn)行整條鏈路。區(qū)別在于腳本對代碼規(guī)范和調(diào)試習(xí)慣提出了更高的要求。4.1 段落注釋與分節(jié)運(yùn)行在編輯器里寫MATLAB腳本最實用的一個功能是代碼分節(jié)Section。如果用兩個百分號加一個空格開始一行注釋比如%% 數(shù)據(jù)加載 data load(mydata.mat); %% 統(tǒng)計分析 mean_val mean(data);編輯器里就會出現(xiàn)兩個分隔條你可以點(diǎn)擊“運(yùn)行節(jié)”按鈕只運(yùn)行當(dāng)前節(jié)。這對動態(tài)調(diào)試極其好用——不需要從頭到尾地跑只跑改動的那一段速度極快。我寫大型腳本時都喜歡用分節(jié)組織邏輯相當(dāng)于給代碼畫出了“段落”比一大坨代碼直接執(zhí)行要清晰得多。4.2 斷點(diǎn)調(diào)試與錯誤提示腳本運(yùn)行報錯時錯誤信息里會直接顯示出錯的行號和錯誤原因。雙擊錯誤信息光標(biāo)會跳到對應(yīng)行。如果你需要觀察執(zhí)行到這個位置的中間變量狀態(tài)就在該行左側(cè)點(diǎn)擊設(shè)置紅點(diǎn)運(yùn)行到那一行前會停下來工作區(qū)里就能看到所有當(dāng)前變量。配合步進(jìn)Step按鈕可以一行一行地觀察數(shù)據(jù)流動。這是工程中定位邏輯錯誤的核心手段前期花點(diǎn)時間學(xué)會斷點(diǎn)后期寫復(fù)雜程序會少掉一堆頭發(fā)。4.3 代碼風(fēng)格與注釋的意義寫腳本和寫文章一樣真正難的從來不是寫出來而是讓別人以及三個月后的自己看得懂。變量命名要有明確語義關(guān)鍵步驟要寫注釋臨時變量不要胡亂覆蓋。我見過很多初學(xué)者腳本沒寫幾行就開始各種縮寫命名到后面自己都分不清哪個是哪個。定義一個規(guī)范有兩條原則就夠了一看就懂和一致性剩下的交給時間。還有一點(diǎn)是關(guān)于腳本文件名的開頭不能是數(shù)字不能和MATLAB內(nèi)置函數(shù)重名不能是中文否則會在函數(shù)調(diào)用的優(yōu)先級上產(chǎn)生問題。以中文命名的腳本偶爾能運(yùn)行但一旦涉及路徑解析就可能出奇怪的錯誤不建議踩這個坑。5. 邏輯、流程控制與函數(shù)式思維腳本每次運(yùn)行都按順序從頭到尾執(zhí)行但實際問題通常需要分支、循環(huán)和封裝。流程控制是讓MATLAB真正走向工程化的關(guān)鍵一步。5.1 if-else條件判斷條件判斷的語法非常簡單if score 60 disp(及格); elseif score 85 disp(優(yōu)秀); else disp(仍需努力); endMATLAB的條件判斷塊以 end 結(jié)束。這里要尤其注意條件表達(dá)式的寫法判斷相等要用賦值用一個等號判斷邏輯同時成立用或關(guān)系用||。在條件里邊寫賦值號是新手常見的錯誤一運(yùn)行就直接報語法錯誤配合調(diào)試模式很快能發(fā)現(xiàn)。5.2 for循環(huán)與向量化取舍循環(huán)不需要復(fù)雜解釋但關(guān)于MATLAB的循環(huán)有一條金律盡可能向量化。先看一段示例循環(huán)s 0; for i 1:100 s s i^2; end這種代碼邏輯上沒有錯但在MATLAB中循環(huán)逐次操作的開銷比向量語法大得多。同樣的問題一行代碼就解決s sum((1:100).^2);在數(shù)據(jù)規(guī)模上千上萬時向量化代碼速度優(yōu)勢是數(shù)量級的差距。MATLAB的底層矩陣運(yùn)算做了大量優(yōu)化循環(huán)卻很難照顧到。我的建議是先用循環(huán)把邏輯跑通如果性能不夠再考慮向量化優(yōu)化。不要在寫第一步時就花大量時間刻意追求向量化代碼的意圖清晰仍然是第一位的。5.3 while與switch的適用場景while循環(huán)適合處理“不知道何時結(jié)束”的邏輯比如直到誤差小于閾值才停止迭代。switch語句適合處理一個變量有多個離散取值的情況比多層if-else嵌套可讀性強(qiáng)得多。實際工程項目里這三種結(jié)構(gòu)經(jīng)?;煊谜莆盏年P(guān)鍵是“在合適的場景選合適的控制流”——分支條件多且有明確的取值就用switch條件少就寫if迭代次數(shù)未知就用while。5.4 函數(shù)文件的開發(fā)習(xí)慣把一段功能封裝成函數(shù)是代碼復(fù)用和抽象的關(guān)鍵。函數(shù)文件的格式如下function avg computeAverage(v) avg sum(v) / length(v); end函數(shù)名要和文件名一致不強(qiáng)制但強(qiáng)建議入口參數(shù)和出口參數(shù)在function行聲明。與腳本的關(guān)鍵區(qū)別在于函數(shù)有自己的局部工作區(qū)不會污染主腳本的變量而腳本會使用共享的工作區(qū)跑完滿屏變量。在函數(shù)設(shè)計上有幾條經(jīng)驗功能要單一不要寫一個幾百行的“萬能函數(shù)”。輸入輸出要明確必要時用nargin和nargout判斷參數(shù)數(shù)量。多用注釋塊說明函數(shù)的用途、輸入輸出的含義方便其他人調(diào)用。6. 數(shù)據(jù)可視化入門畫出第一張能看的圖MATLAB的繪圖能力是它迅速普及的功臣。數(shù)據(jù)在表格里再好看也不如一張曲線圖直觀。plot函數(shù)是所有人接觸的第一個繪圖命令但入門階段要學(xué)的遠(yuǎn)遠(yuǎn)不只是把線畫出來。最簡單的繪圖過程只需兩步。先準(zhǔn)備數(shù)據(jù)再畫圖x 0:0.1:2*pi; y sin(x); plot(x, y);運(yùn)行后圖形窗口會彈出一條正弦曲線。想在同一張圖里畫多條線用hold on命令plot(x, sin(x)); hold on; plot(x, cos(x)); hold off;hold on 的意思是把新曲線疊加到已有圖形上畫完之后要立刻寫 hold off否則后面再畫圖所有曲線都會疊到舊圖上越疊越亂?;A(chǔ)的修改線型、顏色、標(biāo)記點(diǎn)用plot的可選參數(shù)在字符串里指定例如plot(x, sin(x), r--o);表示紅色虛線并在數(shù)據(jù)點(diǎn)上畫圓圈標(biāo)記。這個格式是“線型顏色標(biāo)記”的組合順序不影響挨在一起寫就行。常用的有r紅色、b藍(lán)色、k黑色、--虛線、-.點(diǎn)劃線、o圓點(diǎn)標(biāo)記、*星號標(biāo)記。入門時把這些記牢就能應(yīng)付絕大多數(shù)畫圖場景。要注意的是plot畫完一張圖后下一次執(zhí)行plot會默認(rèn)把舊圖覆蓋掉。如果你想比較兩組不同數(shù)據(jù)記得用figure命令新建一個圖形窗口再畫否則舊圖會被動畫式地抹掉。這個細(xì)節(jié)在工作匯報前臨時跑數(shù)時特別容易踩坑——明明畫了上一張圖執(zhí)行下一段代碼就沒了心態(tài)很容易崩。坐標(biāo)軸、標(biāo)題、圖例這些輔助信息雖然不影響數(shù)據(jù)本身但直接決定了讀者能不能快速看懂。常用命令大概是這些title(正弦與余弦曲線); xlabel(角度 (rad)); ylabel(函數(shù)值); legend(sin, cos); grid on;grid on 會打開網(wǎng)格線對新手判斷數(shù)值很友好。畫完圖之后如果需要保存可以直接在圖形窗口里點(diǎn)擊“保存”菜單或者用代碼saveas(gcf, myfig.png)保存成圖片文件。如果要做論文配圖或匯報插圖推薦導(dǎo)出成矢量格式如SVG或PDF放多大都不會模糊。7. 常見報錯與排錯思路新手最容易踩的六個坑錯誤信息往往是最直接的學(xué)習(xí)資源但前提是你理解它在說什么。我見過太多人一看到紅色報錯就懵了其實大部分問題的產(chǎn)生模式都差不多。這里整理幾個入門階段最高頻的坑值得反復(fù)對照。7.1 索引超出數(shù)組范圍報錯信息長這樣索引超出數(shù)組元素數(shù)目(1)。本質(zhì)原因通常是某個變量的實際大小比自己以為的少或者循環(huán)運(yùn)行時邊界條件算錯了。排查思路在報錯行前查看變量的大小工作區(qū)雙擊變量或運(yùn)行size命令確認(rèn)索引最大值沒有超過size。這是最能鍛煉調(diào)試耐心的一類問題。7.2 未定義函數(shù)或變量常見的措辭是未定義函數(shù)或變量 xxx。原因要么是變量名拼寫錯誤、要么是腳本文件不在當(dāng)前路徑下、要么是函數(shù)文件根本沒保存。第一步檢查拼寫第二步用which命令查看這個名字是否在路徑里。如果是在腳本里首次出現(xiàn)的中間變量也要確認(rèn)它在報錯行之前是否真的被賦值了——符號相同但大小寫不同尤其常見。7.3 矩陣維度必須一致這個報錯幾乎所有人都會遇上。它表示你試圖做逐元素運(yùn)算的兩個矩陣形狀對不上。排查時先看每個矩陣的size再把乘法、除法、冪這些運(yùn)算符前的點(diǎn)檢查一遍。說白了這類錯誤90%跟“少了點(diǎn)”有關(guān)。7.4 下標(biāo)索引必須為正整數(shù)如果在循環(huán)中出現(xiàn)了小數(shù)索引或者負(fù)數(shù)索引MATLAB直接罷工。例如v(0)或v(i-0.5)都會觸發(fā)這個錯誤。排查方向是檢查產(chǎn)生索引的算式有沒有可能出現(xiàn)非整數(shù)、非正數(shù)的情況強(qiáng)烈建議在循環(huán)里用disp打印索引值做監(jiān)視。7.5 腳本與函數(shù)文件的區(qū)別不清晰有的初學(xué)者把函數(shù)定義寫在腳本中間一運(yùn)行就說“函數(shù)定義不能出現(xiàn)在腳本中”。新版MATLAB允許把局部函數(shù)放在腳本文件末尾但必須在所有腳本代碼之后。如果你只是想快速測試一段邏輯可以暫時不用函數(shù)封裝直接寫進(jìn)腳本免得踩這個語法限制。7.6 分號位置和括號匹配代碼里括號不配對也是高頻問題。編輯器會在括號匹配時高亮對應(yīng)的位置如果光標(biāo)放在一個括號上找不到對應(yīng)的另一半就是括號少寫了。這種問題用文本編輯器的自動縮進(jìn)也能幫助識別——結(jié)構(gòu)突然亂掉的地方多半是括號或end出了問題。8. 入門期最容易忽略的好習(xí)慣最后再說幾個在真實使用中才感受到價值的好習(xí)慣雖然和學(xué)習(xí)筆記的“知識點(diǎn)”關(guān)系不大但它們直接決定了后續(xù)學(xué)習(xí)效率。第一隨手清理變量空間。跑新腳本之前在命令行執(zhí)行clear all; close all; clc;是很好的習(xí)慣。第一條清除所有變量第二條關(guān)閉所有圖形窗口第三條清空命令行輸出。這能避免上一次運(yùn)行的殘留變量干擾本次結(jié)果排查邏輯錯誤時尤其有效。不過要注意close all會把所有圖都關(guān)掉別有奇奇怪怪的操作依賴。第二多用help命令。想查一個函數(shù)的用法直接在命令行輸入help 函數(shù)名或者doc 函數(shù)名。后者會打開精美文檔頁面包含語法、示例、參數(shù)說明甚至還有推薦的相關(guān)函數(shù)。與其每次遇到問題就搜索不如先看看官方文檔——很多問題在文檔的example里就寫了答案。長此以往你的查文檔能力慢慢提升了獨(dú)立解決問題的能力也會跟著上來。第三建立自己的代碼模板。繪圖模板、參數(shù)設(shè)置模板、數(shù)據(jù)處理模板每類都存一個基礎(chǔ)版本遇到新問題就復(fù)制出來改。剛開始可能覺得繁瑣后面寫代碼的效率是真的快。第四刻意練習(xí)從“想法到實現(xiàn)”的翻譯能力??吹揭粋€數(shù)學(xué)公式或者一個實際問題先思考它需要哪些輸入、經(jīng)過哪些步驟、最終輸出什么再落到MATLAB代碼上。這種能力比背誦任何函數(shù)都重要。寫代碼最難的從來不是語法而是把問題拆成計算機(jī)可執(zhí)行的步驟。9. 關(guān)于學(xué)習(xí)路徑的一點(diǎn)個人體會入門MATLAB最值得做的事不是背語法而是拿一個自己真正關(guān)心的簡單問題完完整整地跑一遍流程從輸入數(shù)據(jù)開始做點(diǎn)清洗和計算畫出自定義的圖再把項目整理成結(jié)構(gòu)清晰的腳本和函數(shù)。等這一整套流程走過兩三遍基礎(chǔ)知識就不再是孤立的命令拼湊而是長成一張互相連通的網(wǎng)。我剛開始學(xué)時也走了不少彎路。今天看來最大的彎路是花太多時間在“研究面板按鈕”上而真正讓能力飛漲的是動手寫腳本、主動報錯、再主動修錯這幾個回合的循環(huán)。報錯不是壞事每個紅色提示其實都在幫你縮小可能的問題范圍與其害怕它不如學(xué)會翻譯它。這個系列里基礎(chǔ)知識到這里算是一個完整的開始。后面如果要繼續(xù)深入建議著眼于矩陣進(jìn)階操作、數(shù)據(jù)導(dǎo)入導(dǎo)出、腳本模塊化設(shè)計以及更深層的繪圖控制。每往前走一步回頭再看今天這些基礎(chǔ)命令你會發(fā)現(xiàn)自己已經(jīng)在用它解決越來越復(fù)雜的問題了。