本节摘要:程序规模膨胀后,分支循环的组织方式与错误定位能力决定了开发效率。本节讲控制流的结构化组织、
try-catch与error的错误处理策略,以及从打印调试到断点调试的演化,最后给出一个可运行的调试案例。
单脚本时代,出错就盯着一屏代码看。函数把程序拆散后,一个错误的症状出现在 A 文件,根源可能在 C 文件——Matlab 因此陆续长出了结构化控制、错误处理与图形化调试器。这一节的演化线很清楚:先有组织代码的手段,再有定位错误的手段。
分支与循环第 1 章见过语法,这里关心组织。两条经验:一是早返回、少嵌套——条件不满足就 return,别让主逻辑缩进三层;二是循环体抽函数——循环体超过十行,抽出去,循环只剩下骨架:
for k = 1:numel(files) processOne(files{k}); % 细节都在函数里 end
function r = safeRatio(a, b) if b == 0 error('safeRatio:zeroDiv', '分母为零,无法计算比值'); end r = a / b; end
error 带标识符(函数名冒号类别),调用方可以精确捕获。批量处理外部数据时用 try-catch 兜底:
results = cell(numel(files), 1); for k = 1:numel(files) try results{k} = readmatrix(files{k}); catch ME warning('第 %d 个文件失败:%s', k, ME.message); results{k} = []; end end
取舍原则:可预期的参数错误用 error 立刻炸(越早暴露越便宜);不可预期但可容忍的环境问题用 try-catch 接住并记录。反过来用——把 error 全包进 try-catch 让程序假装没坏——是更隐蔽的坑。
打印调试(disp、fprintf)是最古老的方式,至今适合看演变趋势。断点调试则是效率的代际差:
| 手段 | 适用 | 操作 |
|---|---|---|
| 普通断点 | 怀疑某行有错 | 点行号左侧灰线,红点即断点 |
| 条件断点 | 循环第 N 圈才出错 | 右键断点设条件如 k==137 |
| 错误断点 | 错误一闪而过 | 编辑器"运行到错误处暂停" |
| 命令行 | 脚手架 | dbstop if error |

试试手:把 2.2 节的 statFeat 里 x = x(:) 注释掉,传入行向量会在 max(abs(x)) 附近表现异常。在那一行打断点,暂停后在工作区看 x 的形状——形状类 bug 在断点下无所遁形,这是打印调试很难给的信息。
给一个真会咬人的案例。任务:批量计算一批向量的移动平均,代码逻辑看着完全正确:
function y = movAvgBug(x, w) y = zeros(size(x)); for k = 1:numel(x) lo = max(1, k - w + 1); y(k) = mean(x(lo:k)); end end
调用 movAvgBug([1; 2; 3; 4; 100], 3),肉眼看没有语法错误、不抛任何警告,但输出的最后一个值在 35.7 附近——问题在 mean(x(lo:k)):若 x 是列向量,x(lo:k) 是列向量没问题;可一旦调用方传入行向量,中途某处把 x 当作矩阵按列取了均值,结果就静默地错了。修法是函数第一行加 x = x(:);。复盘这个案例的教学价值:没有报错的错误靠"输入形状审计"抓——在函数入口统一形状,是把这类 bug 整类消灭的手段。用条件断点在 k==5 处停下,查看 x(lo:k) 的形状与值,也能当场看穿它。
打印调试对应脚本时代,断点调试对应函数时代,再往上一步是单元测试。函数有了隔离工作区,才"可测"——给定输入断言输出:
% 手工断言:测试即文档 tc = [ safeRatio(10, 2), 5; safeRatio(-6, 3), -2]; assert(isequal(tc(:,1), tc(:,2)), 'safeRatio 基本用例失败') try safeRatio(1, 0); ok = false; catch ok = true; % 预期抛错,没抛才算失败 end assert(ok, '分母为零未触发 error')
这段手工测试不依赖任何框架,却能守住 safeRatio 的两条核心契约:正常算对、非法输入必炸。项目再长大时把它迁到基于类的正式测试框架即可,断言逻辑原样平移。演化的方向始终如一:把"我发现过一次的 bug"变成"机器替我永远盯着的断言"。
三者都表达"不对劲",语义分工却不同。error 声明输入非法、计算无法继续,调用方要么修输入要么显式捕获;warning 声明结果可疑但仍给出,例如数据含缺失值时提示"已按均值填充";断言声明的是开发者自己的中间不变量,理论上永不为假,为假即程序写错。用错位置的典型症状:该 error 的地方用 warning,程序带着坏数据继续跑,最后产出一册看起来正常的错误结果。判据一句话:非法输入 error,可疑结果 warning,程序自身 bug 断言。
调试内容最后补一个工作习惯:定位到 bug 后别急着改,先把"触发条件"写成一句话注释(例如"仅当输入为行向量且长度小于窗口时"),再动手修——这句注释日后就是测试用例的标题。修完立刻补上对应断言,一次调试才算闭环,同一个坑也就再也不会踩第二次。
error,环境异常用 try-catch 记录后继续;fprintf。