3. MATLAB编程基础:脚本与函数、流程控制、向量化编程
说实话,很多初学者觉得MATLAB就是个高级计算器。敲个命令,出个结果,完事。但真正做仿真项目时,你会发现——不会写脚本和函数,寸步难行。
我刚开始带项目时,有个同事把所有代码都堆在一个文件里。2000多行,改一个参数要找半天。后来我帮他重构,拆成几个函数,代码量少了三分之一,跑起来还快了。嗯,这就是我们今天要聊的核心。
3.1 脚本 vs 函数:你该用哪个?
先搞清楚这两个东西的区别。说白了:
- 脚本:就是按顺序执行的命令集合。没有输入输出,所有变量都在工作区里裸奔。
- 函数:有自己的独立空间。输入参数进去,输出结果出来,中间变量不污染工作区。
我个人习惯是:调试用脚本,复用用函数。你想想看,如果一个计算逻辑你要用10次,写成函数绝对比复制粘贴10次脚本要靠谱。
核心区别一览
| 特性 | 脚本 | 函数 |
|---|---|---|
| 变量作用域 | 共享工作区 | 独立局部空间 |
| 输入参数 | 无 | 可以有 |
| 输出结果 | 直接修改工作区变量 | 通过返回值传递 |
| 执行效率 | 较慢(每次解释执行) | 较快(可预编译) |
| 适用场景 | 一次性分析、快速验证 | 模块化、复用逻辑 |
举个例子。假设你要计算一组数据的均值和标准差:
% 脚本方式:直接操作
data = [1, 3, 5, 7, 9];
avg = mean(data);
std_val = std(data);
fprintf('均值: %.2f, 标准差: %.2f\n', avg, std_val);
% 函数方式:封装成模块
function [avg, std_val] = calcStats(data)
avg = mean(data);
std_val = std(data);
end
我在项目中遇到过一个问题:脚本里用了很多临时变量,结果调试到一半,工作区里全是a,b,c,d...根本分不清哪个是哪个。后来全部改成函数调用,清爽多了。
3.2 流程控制:if/for/while 的正确打开方式
流程控制是编程的骨架。但很多人用得很随意,导致代码又慢又难读。
3.2.1 if-elseif-else:别嵌套太深
我曾经接手过一个项目,一个if语句嵌套了7层。我看了一下午才理清逻辑。后来我重构时发现,大部分情况可以用逻辑运算符合并条件。
% 糟糕的写法:嵌套地狱
if x > 0
if y > 0
result = '第一象限';
else
result = '第四象限';
end
else
if y > 0
result = '第二象限';
else
result = '第三象限';
end
end
% 好的写法:逻辑清晰
if x > 0 && y > 0
result = '第一象限';
elseif x < 0 && y > 0
result = '第二象限';
elseif x < 0 && y < 0
result = '第三象限';
else
result = '第四象限';
end
我的小技巧:if语句超过3个分支,考虑用switch-case。超过5个分支,考虑用查表法(后面会讲)。
3.2.2 for循环:预分配内存是关键
很多新手写for循环,数组越变越大。这在数据量小时没问题,但仿真数据一上来——内存爆炸,速度慢得像蜗牛。
% 错误示范:动态增长
tic;
data = [];
for i = 1:10000
data = [data, i^2]; % 每次循环都重新分配内存
end
toc; % 耗时约0.5秒
% 正确做法:预分配
tic;
data = zeros(1, 10000);
for i = 1:10000
data(i) = i^2; % 直接赋值
end
toc; % 耗时约0.001秒
看到了吗?差了500倍!我当年做信号处理仿真时,就因为没预分配,一个跑3小时的仿真,优化后只要40秒。嗯,这个坑我替你们踩过了。
3.2.3 while循环:小心死循环
while循环我用的不多,主要用在迭代收敛的场景。但一定要设置最大迭代次数,否则一旦条件不满足,程序就卡死了。
% 安全的while循环
maxIter = 1000;
iter = 0;
tolerance = 1e-6;
error = inf;
while error > tolerance && iter < maxIter
% 迭代计算...
error = abs(new_value - old_value);
iter = iter + 1;
end
if iter >= maxIter
warning('达到最大迭代次数,可能未收敛');
end
避坑指南:我曾经写过一个while循环,忘记更新迭代变量,结果程序跑了一整夜。第二天发现CPU 100%,MATLAB无响应。从那以后,我每个while循环都加计数器。
3.3 向量化编程:让MATLAB飞起来
这是MATLAB最强大的特性,没有之一。很多从C/C++转过来的朋友,习惯用for循环处理数据。但在MATLAB里,能用向量化就别用循环。
为什么会这样?因为MATLAB底层是高度优化的矩阵运算库(BLAS/LAPACK)。你用向量化操作,相当于直接调用这些底层库;你用for循环,MATLAB就要逐元素解释执行,慢得不是一星半点。
3.3.1 基本向量化操作
% 场景:计算 y = x^2 + 2*x + 1
% 循环方式(慢)
x = 0:0.01:100;
y = zeros(size(x));
for i = 1:length(x)
y(i) = x(i)^2 + 2*x(i) + 1;
end
% 向量化方式(快,约快100倍)
x = 0:0.01:100;
y = x.^2 + 2*x + 1; % 注意点乘
3.3.2 逻辑索引:条件筛选的利器
我处理传感器数据时,经常要筛选出异常值。用逻辑索引一行搞定:
% 筛选出所有大于3倍标准差的数据点
data = randn(1, 1000);
threshold = 3 * std(data);
outliers = data(abs(data) > threshold); % 逻辑索引
% 替换异常值为中位数
median_val = median(data);
data(abs(data) > threshold) = median_val;
3.3.3 矩阵运算代替循环
很多复杂的计算,其实都可以用矩阵乘法搞定。我记得有一次做图像处理,需要计算每个像素的邻域均值。用循环写了30行,用卷积操作3行搞定。
% 计算两个向量的欧氏距离矩阵
% 循环方式
A = [1; 2; 3];
B = [4; 5; 6];
dist_loop = zeros(length(A), length(B));
for i = 1:length(A)
for j = 1:length(B)
dist_loop(i,j) = sqrt((A(i)-B(j))^2);
end
end
% 向量化方式(利用矩阵扩展)
dist_vec = sqrt((A - B').^2); % 一行搞定
向量化编程的核心原则
- 用点运算(.^, .*, ./)代替逐元素循环
- 用逻辑索引代替条件判断循环
- 用矩阵乘法代替嵌套循环
- 用内置函数(sum, mean, diff等)代替手写循环
3.4 本章知识体系
下面这张图是我梳理的本章核心逻辑。你可以把它当作一个检查清单:
3.5 实战建议
学完这些知识,我建议你立刻做一件事:打开你之前写过的MATLAB代码,看看有多少for循环可以改成向量化操作。我保证你会惊讶于性能的提升。
另外,写函数时养成好习惯:每个函数只做一件事。如果一个函数超过50行,考虑拆分成更小的函数。这样调试起来方便,别人看你的代码也一目了然。
我的个人习惯:每个函数开头都写一段注释,说明输入输出和功能。哪怕这个函数只有我自己用。因为三个月后,你再看自己写的代码,就跟看别人写的一样陌生。
好了,这一章的内容就到这里。记住:脚本是快速验证的工具,函数是模块化复用的基石,向量化是性能提升的利器。把这三点吃透,你的MATLAB编程水平会上一个大台阶。
公众号:蓝海资料掘金营,微信deep3321