从主函数中清除局部函数中的持久变量
Clear persistent variables in local functions from within the main function
我有一个代码由一个包含多个函数的文件组成,其中一些函数使用 persistent
变量。为了使其正常工作,持久变量必须为空。
在多功能文件中有clear永久变量的记录方法,例如:
clear functionName % least destructive
clear functions % more destructive
clear all % most destructive
不幸的是,我不能保证用户记得在调用函数之前清除persistent
变量,所以我正在探索在代码开头执行清除操作的方法。为了说明这个问题,请考虑以下示例:
function clearPersistent(methodId)
if ~nargin, methodId = 0; end
switch methodId
case 0
% do nothing
case 1
clear(mfilename);
case 2
eval(sprintf('clear %s', mfilename));
case 3
clear functions;
case 4
clear all;
end
subfunction();
subfunction();
end
function [] = subfunction()
persistent val
if isempty(val)
disp("val is empty");
val = 123;
else
disp("val is not empty");
end
end
当第一个运行这个时,我们得到:
>> clearPersistent
val is empty
val is not empty
我希望此时再次使用 运行 函数,任何非 0 输入都会导致 val
变量被清除,但是唉 - 事实并非如此.设置 val
后,除非我们使用顶部代码段 外部 中显示的替代方案之一,或者修改 .m
文件,否则它会保持设置状态。
我的问题:是否可以从主函数体内清除子函数中的持久变量,如果可以,如何清除?
换句话说,我正在寻找一些可以在调用子函数之前放入 clearPersistent
的代码,以便输出始终如一:
val is empty
val is not empty
P.S.
这是一个相关的过去问题(不涉及此特定用例):List/view/clear persistent variables in Matlab.
我知道重写代码以完全不使用 persistent
变量的可能性(例如,通过传递数据,使用 appdata
,添加 'clear'
所有子功能的标志,等等)。
请注意,编辑函数的源代码并保存会隐式清除它(连同所有持久变量)。
我知道文档指出“clear
函数不会清除局部或嵌套函数中的持久变量。
问题的其他背景:
实际代码结构如下:
Main function (called once)
└ Global optimization solver (called once)
└ Objective function (called an unknown N≫1 times)
└ 1st function that uses persistents
└ 2nd function that uses persistents
正如评论中提到的,一些变量被持久化的原因有几个:
- 松耦合/SoC:objective函数不需要知道子函数是如何工作的。
- 封装:这是一个实现细节。持久变量不需要存在于使用它们的函数范围之外(即没有其他人需要它们)。
- 性能:持久变量包含计算成本相当高的矩阵,但每次调用主函数时只需要执行一次此操作。
使用持久变量的一个(副作用?)效果是使整个代码有状态(有两种状态:before 和 after昂贵的计算)。最初的问题源于这样一个事实,即状态在主函数调用之间没有被正确重置,导致运行依赖于使用先前(因此无效)配置创建的状态。
可以通过在主函数中计算一次性值(目前只解析用户提供的配置,调用求解器,最后 stores/displays 输出),然后传递它们来避免有状态与用户配置一起进入 objective 函数,然后将它们传递给子函数。这种方法解决了状态清除问题,但损害了封装并增加了耦合,进而可能损害可维护性。
不幸的是,objective 函数没有 flag that says 'init'
etc.,所以我们不知道它是为第 1st 还是第 n[=第 85=]th 次,我们自己没有跟踪(又名 state)。
理想的解决方案具有以下几个特性:
- 计算昂贵的数量一次。
- 成为无国籍人。
- 不传递不相关的数据(即“需要了解基础”;各个功能工作区仅包含 他们 需要的数据)。
clear fname
和 clear functions
从内存中删除 M 文件。下次你 运行 这个函数时,它会再次从磁盘读取,解析并编译成字节码。因此,您减慢了函数的下一次执行速度。
因此,从一个函数中清除一个函数或子函数是行不通的。您正在 运行 调用该函数,您无法从内存中清除它的文件。
我的解决方案是向 subfunction
添加一个选项以清除其持久变量,如下所示:
function clearPersistent()
subfunction('clear');
subfunction();
subfunction();
end
function [] = subfunction(option)
persistent val
if nargin>0 && ischar(option) && strcmp(option,'clear')
val = [];
return
end
if isempty(val)
disp("val is empty");
val = 123;
else
disp("val is not empty");
end
end
当然,您可以在调用 subfunction('init')
时初始化您的值。
可能适用于您的用例的不同解决方案是将 val
的计算与其使用分开。我会发现这比任何其他解决方案都更容易阅读,而且性能也会更高。
function main()
val = computeval();
subfunction(val);
subfunction(val);
end
鉴于您的编辑,您可以将 objective 函数放在一个单独的文件中(在 private
子目录中)。您将能够clear
它。
持久变量的替代方法是创建一个用户 class,它具有计算昂贵状态的构造函数,以及另一种计算 objective 函数的方法。这也可以是 private
子目录中的 classdef
文件。我认为这更好,因为您不需要记住调用 clear
.
在这两种情况下,您不再拥有包含所有代码的单个文件。我认为您需要放弃这两个理想之一:要么破坏数据封装,要么将代码拆分成两个文件(代码封装?)。
为什么不使用全局变量?
您可以创建一个包含变量的全局结构,并且可以使用 variable_manager
:
对其进行管理
function main
variable_manager('init')
subfunction1()
subfunction2()
end
function variable_manager(action)
global globals
switch action
case 'init'
globals = struct('val',[],'foo',[]);
case 'clear'
globals = structfun(@(x)[],globals,'UniformOutput', false);
% case ....
% ...
end
end
function subfunction1
global globals
if isempty(globals.val)
disp("val is empty");
globals.val = 123;
else
disp("val is not empty");
end
end
function subfunction2
global globals
if isempty(globals.foo)
disp("foo is empty");
globals.foo = 321;
else
disp("foo is not empty");
end
end
如问题中所述,其中一种可能性是使用 appdata
,这与 global
没有太大区别(至少在将它们与 "object 0
" 相关联时 - 这是MATLAB 实例本身)。避免 "collisions" 与其他 scripts/functions/etc。我们引入了一个随机字符串(如果我们在每个使用这种存储技术的函数中生成一个字符串,它几乎可以保证不会发生冲突)。这种方法的主要缺点是字符串必须在多个地方进行硬编码,或者应该更改代码的结构,以便使用此 appdata
的函数嵌套在定义它的函数中。
这两种写法是:
function clearPersistent()
% Initialization - clear the first time:
clearAppData();
% "Business logic"
subfunction();
subfunction();
% Clear again, just to be sure:
clearAppData();
end % clearPersistent
function [] = subfunction()
APPDATA_NAME = "pZGKmHt6HzkkakvdfLV8"; % Some random string, to avoid "global collisions"
val = getappdata(0, APPDATA_NAME);
if isempty(val)
disp("val is empty");
val = 123;
setappdata(0, APPDATA_NAME, val);
else
disp("val is not empty");
end
end % subfunction
function [] = clearAppData()
APPDATA_NAME = "pZGKmHt6HzkkakvdfLV8";
if isappdata(0, APPDATA_NAME)
rmappdata(0, APPDATA_NAME);
end
end % clearAppData
和:
function clearPersistent()
APPDATA_NAME = "pZGKmHt6HzkkakvdfLV8";
% Initialization - clear the first time:
clearAppData();
% "Business logic"
subfunction();
subfunction();
% Clear again, just to be sure:
clearAppData();
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [] = subfunction()
val = getappdata(0, APPDATA_NAME);
if isempty(val)
disp("val is empty");
val = 123;
setappdata(0, APPDATA_NAME, val);
else
disp("val is not empty");
end
end % subfunction
function [] = clearAppData()
if isappdata(0, APPDATA_NAME)
rmappdata(0, APPDATA_NAME);
end
end % clearAppData
end % clearPersistent
我有一个代码由一个包含多个函数的文件组成,其中一些函数使用 persistent
变量。为了使其正常工作,持久变量必须为空。
在多功能文件中有clear永久变量的记录方法,例如:
clear functionName % least destructive
clear functions % more destructive
clear all % most destructive
不幸的是,我不能保证用户记得在调用函数之前清除persistent
变量,所以我正在探索在代码开头执行清除操作的方法。为了说明这个问题,请考虑以下示例:
function clearPersistent(methodId)
if ~nargin, methodId = 0; end
switch methodId
case 0
% do nothing
case 1
clear(mfilename);
case 2
eval(sprintf('clear %s', mfilename));
case 3
clear functions;
case 4
clear all;
end
subfunction();
subfunction();
end
function [] = subfunction()
persistent val
if isempty(val)
disp("val is empty");
val = 123;
else
disp("val is not empty");
end
end
当第一个运行这个时,我们得到:
>> clearPersistent
val is empty
val is not empty
我希望此时再次使用 运行 函数,任何非 0 输入都会导致 val
变量被清除,但是唉 - 事实并非如此.设置 val
后,除非我们使用顶部代码段 外部 中显示的替代方案之一,或者修改 .m
文件,否则它会保持设置状态。
我的问题:是否可以从主函数体内清除子函数中的持久变量,如果可以,如何清除?
换句话说,我正在寻找一些可以在调用子函数之前放入 clearPersistent
的代码,以便输出始终如一:
val is empty
val is not empty
P.S.
这是一个相关的过去问题(不涉及此特定用例):List/view/clear persistent variables in Matlab.
我知道重写代码以完全不使用
persistent
变量的可能性(例如,通过传递数据,使用appdata
,添加'clear'
所有子功能的标志,等等)。请注意,编辑函数的源代码并保存会隐式清除它(连同所有持久变量)。
我知道文档指出“
clear
函数不会清除局部或嵌套函数中的持久变量。
问题的其他背景:
实际代码结构如下:
Main function (called once)
└ Global optimization solver (called once)
└ Objective function (called an unknown N≫1 times)
└ 1st function that uses persistents
└ 2nd function that uses persistents
正如评论中提到的,一些变量被持久化的原因有几个:
- 松耦合/SoC:objective函数不需要知道子函数是如何工作的。
- 封装:这是一个实现细节。持久变量不需要存在于使用它们的函数范围之外(即没有其他人需要它们)。
- 性能:持久变量包含计算成本相当高的矩阵,但每次调用主函数时只需要执行一次此操作。
使用持久变量的一个(副作用?)效果是使整个代码有状态(有两种状态:before 和 after昂贵的计算)。最初的问题源于这样一个事实,即状态在主函数调用之间没有被正确重置,导致运行依赖于使用先前(因此无效)配置创建的状态。
可以通过在主函数中计算一次性值(目前只解析用户提供的配置,调用求解器,最后 stores/displays 输出),然后传递它们来避免有状态与用户配置一起进入 objective 函数,然后将它们传递给子函数。这种方法解决了状态清除问题,但损害了封装并增加了耦合,进而可能损害可维护性。
不幸的是,objective 函数没有 flag that says 'init'
etc.,所以我们不知道它是为第 1st 还是第 n[=第 85=]th 次,我们自己没有跟踪(又名 state)。
理想的解决方案具有以下几个特性:
- 计算昂贵的数量一次。
- 成为无国籍人。
- 不传递不相关的数据(即“需要了解基础”;各个功能工作区仅包含 他们 需要的数据)。
clear fname
和 clear functions
从内存中删除 M 文件。下次你 运行 这个函数时,它会再次从磁盘读取,解析并编译成字节码。因此,您减慢了函数的下一次执行速度。
因此,从一个函数中清除一个函数或子函数是行不通的。您正在 运行 调用该函数,您无法从内存中清除它的文件。
我的解决方案是向 subfunction
添加一个选项以清除其持久变量,如下所示:
function clearPersistent()
subfunction('clear');
subfunction();
subfunction();
end
function [] = subfunction(option)
persistent val
if nargin>0 && ischar(option) && strcmp(option,'clear')
val = [];
return
end
if isempty(val)
disp("val is empty");
val = 123;
else
disp("val is not empty");
end
end
当然,您可以在调用 subfunction('init')
时初始化您的值。
可能适用于您的用例的不同解决方案是将 val
的计算与其使用分开。我会发现这比任何其他解决方案都更容易阅读,而且性能也会更高。
function main()
val = computeval();
subfunction(val);
subfunction(val);
end
鉴于您的编辑,您可以将 objective 函数放在一个单独的文件中(在 private
子目录中)。您将能够clear
它。
持久变量的替代方法是创建一个用户 class,它具有计算昂贵状态的构造函数,以及另一种计算 objective 函数的方法。这也可以是 private
子目录中的 classdef
文件。我认为这更好,因为您不需要记住调用 clear
.
在这两种情况下,您不再拥有包含所有代码的单个文件。我认为您需要放弃这两个理想之一:要么破坏数据封装,要么将代码拆分成两个文件(代码封装?)。
为什么不使用全局变量?
您可以创建一个包含变量的全局结构,并且可以使用 variable_manager
:
function main
variable_manager('init')
subfunction1()
subfunction2()
end
function variable_manager(action)
global globals
switch action
case 'init'
globals = struct('val',[],'foo',[]);
case 'clear'
globals = structfun(@(x)[],globals,'UniformOutput', false);
% case ....
% ...
end
end
function subfunction1
global globals
if isempty(globals.val)
disp("val is empty");
globals.val = 123;
else
disp("val is not empty");
end
end
function subfunction2
global globals
if isempty(globals.foo)
disp("foo is empty");
globals.foo = 321;
else
disp("foo is not empty");
end
end
如问题中所述,其中一种可能性是使用 appdata
,这与 global
没有太大区别(至少在将它们与 "object 0
" 相关联时 - 这是MATLAB 实例本身)。避免 "collisions" 与其他 scripts/functions/etc。我们引入了一个随机字符串(如果我们在每个使用这种存储技术的函数中生成一个字符串,它几乎可以保证不会发生冲突)。这种方法的主要缺点是字符串必须在多个地方进行硬编码,或者应该更改代码的结构,以便使用此 appdata
的函数嵌套在定义它的函数中。
这两种写法是:
function clearPersistent()
% Initialization - clear the first time:
clearAppData();
% "Business logic"
subfunction();
subfunction();
% Clear again, just to be sure:
clearAppData();
end % clearPersistent
function [] = subfunction()
APPDATA_NAME = "pZGKmHt6HzkkakvdfLV8"; % Some random string, to avoid "global collisions"
val = getappdata(0, APPDATA_NAME);
if isempty(val)
disp("val is empty");
val = 123;
setappdata(0, APPDATA_NAME, val);
else
disp("val is not empty");
end
end % subfunction
function [] = clearAppData()
APPDATA_NAME = "pZGKmHt6HzkkakvdfLV8";
if isappdata(0, APPDATA_NAME)
rmappdata(0, APPDATA_NAME);
end
end % clearAppData
和:
function clearPersistent()
APPDATA_NAME = "pZGKmHt6HzkkakvdfLV8";
% Initialization - clear the first time:
clearAppData();
% "Business logic"
subfunction();
subfunction();
% Clear again, just to be sure:
clearAppData();
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [] = subfunction()
val = getappdata(0, APPDATA_NAME);
if isempty(val)
disp("val is empty");
val = 123;
setappdata(0, APPDATA_NAME, val);
else
disp("val is not empty");
end
end % subfunction
function [] = clearAppData()
if isappdata(0, APPDATA_NAME)
rmappdata(0, APPDATA_NAME);
end
end % clearAppData
end % clearPersistent