本节摘要:Matlab 的变量默认是双精度矩阵,运算符默认按矩阵语义解释。本节讲清变量与数据类型、两类运算符的区分(矩阵运算与数组运算)、控制流语句,重点是与通用语言不同的语义规则。
从 Python 或 C 迁移过来的人,几乎都在两个地方翻过车:一是以为 2^1/2 会得到 2 的平方根(实际从左到右算,结果就是 1);二是用 * 乘两个维度不匹配的矩阵直接报错,因为 Matlab 的 * 是矩阵乘法,不是逐元素乘。这些"坑"不是设计缺陷——在 1980 年代的定位里,Matlab 的用户默认懂数值线性代数,运算符自然选了数学语义。
Matlab 是动态类型语言,变量无需声明。但"默认双精度矩阵"这件事从第一天就没变过:
x = 3.14 % 1x1 矩阵,double 类型 v = [1 2 3] % 行向量 M = [1 2; 3 4] % 2x2 矩阵,分号换行 s = 'matlab' % 字符数组(老派写法) str = "matlab" % string 类型(R2016b 引入,演化痕迹明显) c = {1, 'a', [1 2]} % 元胞数组:类型混杂的容器
演化痕迹值得注意:string 双引号类型出现得很晚,此前字符串处理全靠字符数组,这也是大量老代码里 strcat、sprintf 遍地的原因。新代码建议统一用 string。
类型判断用 class(x),转换用 double、int32 这类函数。工程上一个常见决策是图像数据:uint8 存储省内存,但运算前要转 double,否则 uint8 的饱和截断会把 250+10 变成 255。
| 运算 | 矩阵语义 | 数组语义(逐元素) |
|---|---|---|
| 乘 | A*B |
A.*B |
| 除 | A\b(左除)、/(右除) |
A./B |
| 幂 | A^2 |
A.^2 |
| 转置 | A'(共轭转置) |
A.'(普通转置) |
一个点,区分了整门线性代数和逐元素运算。写数值代码时的经验法则:想清楚这一步在数学上是矩阵变换还是逐点函数,语法自然就对了。
A = [1 2; 3 4]; B = A * A % 矩阵平方:A两次作用 C = A .^ 2 % 每个元素自己平方
if/for/while 语法上和 C 系语言接近,但两点不同:块用 end 关闭而非花括号;for 遍历的是矩阵的每一列。
for k = 1:5 % 1:5 生成行向量,k 依次取每列 fprintf('k = %d\n', k); end n = 0; while n < 3 n = n + 1; end
for 遍历列这个设定看似冷僻,却是矩阵中心主义的自然结果——连循环变量都是矩阵。第 6 章会讲为什么在 Matlab 里应当尽量少写显式循环,向量化才是母语写法。
⚠️ 常见坑:变量名覆盖内置函数。给变量取名
sum、mean、length之后,这些函数在你clear之前全部失效,报错信息还往往指向别处。
把常用类型放进一张表,工程里怎么选就清楚了:
| 类型 | 典型构造 | 适合存什么 | 注意点 |
|---|---|---|---|
| double | 默认 | 数值计算主力 | 占 8 字节/元素,大数组费内存 |
| single | single(x) |
大规模数据、GPU 计算 | 精度约 7 位有效数字 |
| uint8 | uint8(x) |
图像像素、传感器原始值 | 溢出饱和到 255,不回绕 |
| logical | A > 10 |
掩码、条件筛选 | 可当索引用,占 1 字节 |
| cell | {...} |
异构集合、变长表格 | 取内容用花括号 |
| struct | s.name = ... |
带字段名的记录 | 字段可再嵌套 |
整数类型的饱和行为值得单独演示一次,印象最深刻:
a = uint8(250); b = a + 10 % 结果是 255,不是 260 c = double(a) + 10 % 先转 double 再算,得到 260
第一行输出 255 而不是 260,因为 uint8 到顶就截住。图像处理里把这一步忘了,几百行代码的统计结果会整体偏低且无任何报错——这是静默错误,比崩溃更难查。
初学者最容易混的两对括号:圆括号取元胞"盒子",花括号取盒子里面的"东西"。
c = {rand(3), 'text'}; box = c(1); % 1x1 元胞,里面装着矩阵 val = c{1}; % 3x3 矩阵本身 size(box) % 输出 1 1 size(val) % 输出 3 3
结构体则走另一条路,字段名即语义:
run.trial = 1; run.data = rand(100,2); run.note = "baseline"; runs = repmat(run, 4, 1); % 批量做 4 次实验记录 runs(2).trial = 2;
选型经验:元素结构一致、想整体做运算,用矩阵;元素类型各异但位置有序,用元胞;每个元素有明确字段语义、要按名字访问,用结构体数组。实验记录这类场景,结构体数组的可读性远胜元胞。
while 配浮点条件是另一个经典雷区。下面这段代码在有些机器上会多循环一次甚至不终止:
x = 0; n = 0; while x < 1 x = x + 0.1; n = n + 1; end % 期望 n=10,实际常常是 11:0.1 的二进制表示略大
原因是 0.1 在二进制里不能精确表示,十次累加后 x 略大于 1 的判定会推迟或提前。工程写法是给比较留容差:用 while abs(x - 1) > 1e-9 这类带容差的条件,或者干脆改用整数计数控制循环。
* 是矩阵乘、.* 是逐元素乘,除法与幂同理;A\b 直接解方程,这是从 LINPACK 时代继承的看家语法;end 收尾,避开内置函数名做变量名。