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);  % 一行搞定

向量化编程的核心原则

  1. 用点运算(.^, .*, ./)代替逐元素循环
  2. 用逻辑索引代替条件判断循环
  3. 用矩阵乘法代替嵌套循环
  4. 用内置函数(sum, mean, diff等)代替手写循环

3.4 本章知识体系

下面这张图是我梳理的本章核心逻辑。你可以把它当作一个检查清单:

MATLAB编程基础:知识体系 脚本与函数 脚本:顺序执行,共享工作区 函数:独立空间,输入输出 选择原则:调试用脚本,复用用函数 流程控制 if-elseif-else:避免嵌套过深 for循环:预分配内存 while循环:设置最大迭代 向量化编程 点运算代替循环 逻辑索引筛选数据 矩阵运算替代嵌套循环 核心原则 能向量化就不循环,能函数化就不脚本 预分配内存,设置迭代上限,避免嵌套过深

3.5 实战建议

学完这些知识,我建议你立刻做一件事:打开你之前写过的MATLAB代码,看看有多少for循环可以改成向量化操作。我保证你会惊讶于性能的提升。

另外,写函数时养成好习惯:每个函数只做一件事。如果一个函数超过50行,考虑拆分成更小的函数。这样调试起来方便,别人看你的代码也一目了然。

我的个人习惯:每个函数开头都写一段注释,说明输入输出和功能。哪怕这个函数只有我自己用。因为三个月后,你再看自己写的代码,就跟看别人写的一样陌生。

好了,这一章的内容就到这里。记住:脚本是快速验证的工具,函数是模块化复用的基石,向量化是性能提升的利器。把这三点吃透,你的MATLAB编程水平会上一个大台阶。


公众号:蓝海资料掘金营,微信deep3321