Skip to content

MATLAB - 全局变量

MATLAB - 全局变量及现代替代方案

Section titled “MATLAB - 全局变量及现代替代方案”

在 MATLAB 中,global 变量是一种可以从代码的任何部分访问和修改的变量,包括从不同的函数、基础工作区和脚本中。您可以使用 global 关键字将变量声明为全局变量。

然而,在现代软件工程中,强烈不推荐使用全局变量。虽然它们看起来方便数据共享,但通常会导致代码难以阅读、调试和维护。本教程将首先解释全局变量的工作原理,然后介绍在 MATLAB 程序中共享数据的更好、更现代的替代方案。

要使用全局变量,您必须在每个需要访问它的工作区中将其声明为 global。

global MY_CONSTANT
function myFunction()
global MY_CONSTANT; % 在函数中声明以访问
disp(['The value is: ', num2str(MY_CONSTANT)]);
end
  • 初始化:如果您声明了一个尚不存在的全局变量,它将被初始化为一个空的 0x0 双精度矩阵。
  • 名称冲突:如果您将一个变量声明为 global 时,局部工作区中已存在同名变量,MATLAB 会发出警告。然后局部变量会被清除,其值被全局变量的值取代。
  • 共享状态:所有将同一变量声明为 global 的函数都引用的是该变量在内存中的同一个实例。在一个函数中的更改会立即影响所有其他函数。

全局变量的危险(为什么应该避免使用它们)

Section titled “全局变量的危险(为什么应该避免使用它们)”
  • 隐藏依赖:函数的行为可能被完全不同文件中的代码改变,使程序逻辑难以追踪。这通常被称为“远距离幽灵行为”(spooky action at a distance)。
  • 调试困难:如果一个全局变量的值不正确,您必须搜索整个代码库才能找到它在哪里被修改。使用现代替代方案,数据流是明确的。
  • 可重用性降低:依赖于全局变量的函数无法在其他项目中轻易重用,因为它要求正确设置全局状态。
  • 命名冲突:随着项目的增长,意外地将相同的全局变量名用于不同目的的风险增加,导致难以发现的错误。

与其使用全局变量,不如考虑这些健壮且可维护的数据共享策略,以便在函数之间共享数据。

1. 将数据作为函数参数传递(最佳默认选择)

Section titled “1. 将数据作为函数参数传递(最佳默认选择)”

最直接和推荐的方法是将数据作为输入参数传递给函数,并以输出参数的形式获取结果。这使得数据流明确,并使函数自包含。

% main_script.m
appConfig.username = 'admin';
appConfig.version = '2.1';
data = [1, 2, 3, 4, 5];
processedData = processMyData(data, appConfig);
disp(processedData);
% -------------------------------------
% processMyData.m
function output = processMyData(inputData, config)
% 此函数只使用传递给它的数据。
disp(['Processing data for user: ', config.username]);
output = inputData * 2; % 示例处理
end

定义在另一个函数(父函数)内部的函数可以访问和修改父函数工作区中的变量。这对于创建在有限、定义明确的作用域内共享状态的辅助函数非常有用。

% main_function.m
function main_function()
counter = 0;
% 调用嵌套函数
incrementCounter();
incrementCounter();
disp(['Final counter value: ', num2str(counter)]);
% -- 嵌套函数定义 --
function incrementCounter()
% 这个嵌套函数可以访问其父函数中的 'counter'。
counter = counter + 1;
disp(['Counter incremented to: ', num2str(counter)]);
end
end

对于复杂应用程序,将共享数据和操作这些数据的函数封装在句柄类中是一种优秀且可扩展的方法。当您传递句柄类对象时,您传递的是对其的引用,因此在一个函数中所做的更改会反映到所有地方。

% DataManager.m(保存为 DataManager.m 文件)
classdef DataManager < handle
properties
Data
Log
end
methods
function obj = DataManager(initialData)
obj.Data = initialData;
obj.Log = {'Initialized'};
end
function process(obj)
obj.Data = obj.Data .^ 2;
obj.Log{end+1} = 'Data processed';
end
end
end
% --- 在您的脚本中 ---
myDataManager = DataManager([1, 2, 3]);
process(myDataManager); % 修改对象
disp(myDataManager.Data); % 显示 [1, 4, 9]
disp(myDataManager.Log); % 显示 {'Initialized', 'Data processed'}

如果您绝对必须使用全局变量(例如,在遗留代码中),您可以从工作区中清除它们。清除一个全局变量会将其从所有声明了它的工作区中移除。

% 清除特定的全局变量
clear global MY_CONSTANT
% 清除所有全局变量(请谨慎使用)
clearvars -global
  • 避免使用 global:将编写不使用全局变量的代码作为您的目标。
  • 优先使用函数参数:始终优先选择通过函数参数和返回值显式传递数据。
  • 使用嵌套函数实现共享作用域:它们是限制共享数据范围到单个父函数的好方法。
  • 复杂场景采用面向对象编程(OOP):对于具有复杂共享状态的应用程序,学习并使用句柄类。