1. 从脚本到函数

在 MATLAB 中,脚本(script)和函数(function)是两种最基本的程序组织单元。初学者往往把两者混为一谈,因为它们都能执行计算、都能保存为 .m 文件。但二者在工作区管理、参数传递和复用方式上存在本质差异,理解这些差异是从“会写代码”走向“会设计程序”的第一步。

脚本是一串按顺序执行的命令集合,它没有输入参数,也没有输出参数。脚本运行时,其中的变量直接创建在调用者的基础工作区(base workspace)中,脚本结束后这些变量依然存在。这意味着脚本与外部环境是“共享内存”的关系:脚本可以读取你之前定义的变量,也可能无意中覆盖它们。函数则不同,函数拥有自己独立的局部工作区(local workspace),只有通过输入参数传入、通过输出参数返回的数据才能跨越边界。函数内部创建的临时变量在函数返回后自动销毁,不会污染外部环境。

这种隔离性带来三个直接好处:一是可复用,同一个函数可以在不同脚本、不同项目中反复调用,只需关心输入输出契约;二是可测试,给定确定的输入就能验证输出是否符合预期;三是可维护,函数内部的实现细节对外部透明,修改内部逻辑不会影响调用方。脚本适合一次性的探索性计算,函数适合沉淀为可长期使用的计算组件。

函数的基本结构

一个完整的 MATLAB 函数由函数定义行、帮助注释、函数体和 end 关键字组成。函数定义行的语法为:

function [输出1, 输出2] = 函数名(输入1, 输入2)
% 帮助注释:说明函数用途、输入输出含义
    % 函数体
end

其中方括号内的输出参数在只有一个输出时可以省略方括号,输入参数为空时写空括号。函数名应与文件名一致(保存为 函数名.m),否则 MATLAB 调用时会以文件名为准并给出警告。函数定义行下方的连续注释块会被 help 命令读取,是函数对外文档的一部分,应当写清楚每个参数的类型、含义和取值范围。

局部工作区是函数的核心机制。调用函数时,MATLAB 为这次调用创建一个新的工作区,输入参数的值被复制进去;函数体执行过程中产生的所有变量都存在于这个工作区;函数执行到 end 或 return 时,输出参数的值被复制回调用者,局部工作区随之销毁。因此函数内部无法直接访问调用者的变量,调用者也无法看到函数内部的临时变量。若确实需要共享数据,应通过输入输出参数显式传递,而不是依赖全局变量。

实战:把一段计算脚本改写为函数

假设我们有一段用于计算矩形截面梁弯曲应力的脚本,它针对一组固定的梁参数完成计算并打印结果:

% beam_script.m —— 原始脚本
b = 0.2;          % 截面宽度,单位 m
h = 0.4;          % 截面高度,单位 m
M = 15000;        % 弯矩,单位 N·m
I = b * h^3 / 12; % 截面惯性矩,单位 m^4
y = h / 2;        % 到中性轴的最大距离,单位 m
sigma = M * y / I;% 最大弯曲正应力,单位 Pa
fprintf('最大弯曲正应力 = %.2f MPa\n', sigma / 1e6);

这段脚本的问题很明显:参数写死在代码里,换一根梁就要改代码;计算结果只打印不返回,无法参与后续计算;变量 b、h、M、I、y、sigma 全部留在基础工作区。下面把它改写为函数:

function [sigma, I] = beam_bending_stress(b, h, M)
% BEAM_BENDING_STRESS 计算矩形截面梁的最大弯曲正应力
% 输入:
%   b - 截面宽度,单位 m,正数
%   h - 截面高度,单位 m,正数
%   M - 弯矩,单位 N·m
% 输出:
%   sigma - 最大弯曲正应力,单位 Pa
%   I     - 截面惯性矩,单位 m^4
    I = b * h^3 / 12;
    y = h / 2;
    sigma = M * y / I;
end

改写后,函数可以在任意脚本或命令行中调用:

[s1, I1] = beam_bending_stress(0.2, 0.4, 15000);
[s2, I2] = beam_bending_stress(0.25, 0.5, 15000);
fprintf('方案1应力 %.2f MPa,方案2应力 %.2f MPa\n', s1/1e6, s2/1e6);

以示例参数代入计算:方案1的惯性矩 I1 = 0.2×0.4³/12 ≈ 1.0667×10⁻³ m⁴,最大应力 σ1 = 15000×0.2/1.0667×10⁻³ ≈ 2.81×10⁶ Pa,即约 2.81 MPa;方案2的 I2 = 0.25×0.5³/12 ≈ 2.6042×10⁻³ m⁴,σ2 = 15000×0.25/2.6042×10⁻³ ≈ 1.44×10⁶ Pa,即约 1.44 MPa。这些数值属于按公式计算的示例结果,用于说明函数可对不同输入批量返回结果,实际工程中应以真实载荷与截面尺寸重新计算。

改写时的一个实用检查:函数体内是否出现了未在输入参数列表中声明的外部变量?如果有,说明该函数隐式依赖了外部环境,应把它补充为输入参数,否则函数在不同工作区中调用时可能报“未定义变量”错误。

脚本与函数的关键差异对照

对比维度脚本函数
参数无输入输出参数有明确的输入输出参数列表
工作区使用调用者的基础工作区拥有独立的局部工作区
变量生命周期执行后变量保留返回后局部变量销毁
复用方式复制粘贴或 run 调用按名称调用,可传不同参数
适用场景一次性探索、临时计算可复用计算组件、团队协作

下面的图示概括了脚本与函数在数据边界上的本质区别:脚本与基础工作区之间没有边界,变量双向可见;函数则通过输入输出参数形成一道受控的边界,局部工作区内的中间变量被封装在内部。

脚本与函数的工作区边界对比 脚本:无边界,共享基础工作区 基础工作区 b h M I sigma y 脚本执行后变量全部留在基础工作区 函数:受控边界,独立局部工作区 局部工作区(调用时创建) b, h, M I, y sigma 返回后局部变量自动销毁 输入参数 输出参数 只有输入输出参数能跨越函数边界 调用者(脚本或命令行)

从脚本到函数的改写,本质上是一次“边界设计”:把哪些量作为输入、哪些量作为输出、哪些量只作为内部中间结果,决定了函数的复用能力和接口清晰度。建议的实践路径是:先写出能跑通的脚本,确认计算逻辑正确;再识别脚本中随场景变化的量,把它们提升为输入参数;把需要参与后续计算的量提升为输出参数;最后把其余变量留在函数内部作为局部变量。完成这一步,一段一次性脚本就变成了可被反复调用的计算组件。

常见错误:函数文件名与函数名不一致会导致调用失败或调用到错误文件;函数定义行后缺少 end(在包含多个函数的文件中必须显式闭合);在函数内部直接使用脚本中定义的变量,运行时报“未定义变量”。遇到这些报错时,优先检查函数定义行、文件名和参数列表三处。