将匿名函数扩展为字符串
Expanding anonymous function into a string
我有一组匿名函数,我想将它们转换成字符串。通常我只会使用 func2str
,但问题是我希望将变量和内部函数扩展为它们的 "true" 值。我遇到的问题 运行 是 MATLAB 按名称保留这些,但识别值。范例
classdef Bclass
properties
equation
end
function obj = Bclass(inEquation, inValue)
obj.equation = @(t,y) inEquation(t,y) * inValue;
end
function out = getStr(obj)
out = func2str(obj.equation);
end
end
问题是 func2str
调用正在输出 @(t,y) inEquation(t,y) * inValue
,而我实际上希望它输出类似 @(t,y) t*y * 5
的内容,如果我们说 b = Bclass(@(t,y) t*y, 5)
。
有没有办法从 MATLAB 中检索这些变量值?
您可以这样做,但是如果您的问题变得比上面给出的示例更复杂(即更复杂的匿名函数、多个嵌套级别、 ETC。)。您必须像我下面那样使用 functions
function to get information on the function handle, and its behavior may change between releases. Additionally, you'll have to do quite a bit of string manipulation (using functions like regexp
, regexprep
, strsplit
, and strrep
。
我已尝试在此处包含我能采用的最通用的方法,考虑到以下可能性:
inEquation
可以是非匿名函数句柄(即 @times
)。
inEquation
可以简单地按原样传递而无需实际调用。
inEquation
可以在匿名函数中多次调用
inEquation
的输入参数可以与在 obj.equation
中调用的参数命名不同。
obj.equation
可以包含索引操作。
首先,我们将初始化一些变量来模仿您的示例:
f1 = @(m, n) m*n; % Note the different variable names, but it will still work
inEquation = f1;
inValue = 5;
f2 = @(t, y) inEquation(t, y)*inValue; % Function constructed using workspace variables
接下来,我们将获取f2
的函数信息:
s = functions(f2);
varNames = fieldnames(s.workspace{1});
varValues = struct2cell(s.workspace{1});
out = s.function;
workspace
字段包含用于构造 f2
的变量名称和值,而 function
字段是您通过调用 [=39= 获得的字符串] 在 f2
上。我们还需要计算一些东西,以便我们可以正确解析 f2
:
中的左括号和右括号
openIndex = (out == '(');
closeIndex = (out == ')');
parenIndex = cumsum(openIndex-[false closeIndex(1:end-1)]).*(openIndex | closeIndex);
现在,我们将遍历工作区变量,将它们的值转换为字符串(如果可能),并在 out
:
中替换它们
for iVar = 1:numel(varNames)
name = varNames{iVar};
value = varValues{iVar};
if isa(value, 'function_handle') % Workspace variable is a function handle
value = func2str(value);
callIndex = strfind(out, [name, '('])+numel(name);
fcnParts = regexp(value, '@\({1}([^\)])*\){1}(\S)*', 'once', 'tokens');
if isempty(callIndex) % Function handle is not invoked
if isempty(fcnParts) % Non-anonymous function handle (i.e. @times)
value = ['@' value];
end
out = strrep(out, name, value);
elseif isempty(fcnParts) % Invoked function handle (i.e. @times)
out = strrep(out, name, value);
else % Invoked anonymous function handle
for iCall = callIndex
args = out(iCall+(1:find(parenIndex(iCall+1:end) == parenIndex(iCall), 1)-1));
value = regexprep(fcnParts{2}, ...
strcat('(?<!\w)', strsplit(fcnParts{1}, ','), '(?!\w)'), ...
strsplit(args, ','));
out = strrep(out, [name, '(', args, ')'], value);
end
end
elseif isnumeric(value) && isscalar(value) % Workspace variable is a numeric scalar
out = strrep(out, name, num2str(value));
end
end
我们得到了 out
想要的结果:
>> out
out =
@(t,y)t*y*5
请注意,这对于非匿名函数句柄也将按预期工作:
>> f1 = @times;
>> inEquation = f1;
>> inValue = 5;
>> f2 = @(t, y) inEquation(t, y)*inValue;
% Repeat above processing...
>> out
out =
@(t,y)times(t,y)*5
它也适用于一些更复杂的功能:
>> postVolt = @(g, V) -.05*g*(V+80);
>> preIdx = 5;
>> postIdx = 1;
>> index = 6;
>> obj.values = {};
>> f2 = @(t) postVolt(obj.values{preIdx}(index), obj.values{preIdx}(obj.voltIdx{postIdx}));
% Repeat above processing...
>> out
out =
@(t)-.05*obj.values{5}(6)*(obj.values{5}(obj.voltIdx{1})+80)
我有一组匿名函数,我想将它们转换成字符串。通常我只会使用 func2str
,但问题是我希望将变量和内部函数扩展为它们的 "true" 值。我遇到的问题 运行 是 MATLAB 按名称保留这些,但识别值。范例
classdef Bclass
properties
equation
end
function obj = Bclass(inEquation, inValue)
obj.equation = @(t,y) inEquation(t,y) * inValue;
end
function out = getStr(obj)
out = func2str(obj.equation);
end
end
问题是 func2str
调用正在输出 @(t,y) inEquation(t,y) * inValue
,而我实际上希望它输出类似 @(t,y) t*y * 5
的内容,如果我们说 b = Bclass(@(t,y) t*y, 5)
。
有没有办法从 MATLAB 中检索这些变量值?
您可以这样做,但是如果您的问题变得比上面给出的示例更复杂(即更复杂的匿名函数、多个嵌套级别、 ETC。)。您必须像我下面那样使用 functions
function to get information on the function handle, and its behavior may change between releases. Additionally, you'll have to do quite a bit of string manipulation (using functions like regexp
, regexprep
, strsplit
, and strrep
。
我已尝试在此处包含我能采用的最通用的方法,考虑到以下可能性:
inEquation
可以是非匿名函数句柄(即@times
)。inEquation
可以简单地按原样传递而无需实际调用。inEquation
可以在匿名函数中多次调用inEquation
的输入参数可以与在obj.equation
中调用的参数命名不同。obj.equation
可以包含索引操作。
首先,我们将初始化一些变量来模仿您的示例:
f1 = @(m, n) m*n; % Note the different variable names, but it will still work
inEquation = f1;
inValue = 5;
f2 = @(t, y) inEquation(t, y)*inValue; % Function constructed using workspace variables
接下来,我们将获取f2
的函数信息:
s = functions(f2);
varNames = fieldnames(s.workspace{1});
varValues = struct2cell(s.workspace{1});
out = s.function;
workspace
字段包含用于构造 f2
的变量名称和值,而 function
字段是您通过调用 [=39= 获得的字符串] 在 f2
上。我们还需要计算一些东西,以便我们可以正确解析 f2
:
openIndex = (out == '(');
closeIndex = (out == ')');
parenIndex = cumsum(openIndex-[false closeIndex(1:end-1)]).*(openIndex | closeIndex);
现在,我们将遍历工作区变量,将它们的值转换为字符串(如果可能),并在 out
:
for iVar = 1:numel(varNames)
name = varNames{iVar};
value = varValues{iVar};
if isa(value, 'function_handle') % Workspace variable is a function handle
value = func2str(value);
callIndex = strfind(out, [name, '('])+numel(name);
fcnParts = regexp(value, '@\({1}([^\)])*\){1}(\S)*', 'once', 'tokens');
if isempty(callIndex) % Function handle is not invoked
if isempty(fcnParts) % Non-anonymous function handle (i.e. @times)
value = ['@' value];
end
out = strrep(out, name, value);
elseif isempty(fcnParts) % Invoked function handle (i.e. @times)
out = strrep(out, name, value);
else % Invoked anonymous function handle
for iCall = callIndex
args = out(iCall+(1:find(parenIndex(iCall+1:end) == parenIndex(iCall), 1)-1));
value = regexprep(fcnParts{2}, ...
strcat('(?<!\w)', strsplit(fcnParts{1}, ','), '(?!\w)'), ...
strsplit(args, ','));
out = strrep(out, [name, '(', args, ')'], value);
end
end
elseif isnumeric(value) && isscalar(value) % Workspace variable is a numeric scalar
out = strrep(out, name, num2str(value));
end
end
我们得到了 out
想要的结果:
>> out
out =
@(t,y)t*y*5
请注意,这对于非匿名函数句柄也将按预期工作:
>> f1 = @times;
>> inEquation = f1;
>> inValue = 5;
>> f2 = @(t, y) inEquation(t, y)*inValue;
% Repeat above processing...
>> out
out =
@(t,y)times(t,y)*5
它也适用于一些更复杂的功能:
>> postVolt = @(g, V) -.05*g*(V+80);
>> preIdx = 5;
>> postIdx = 1;
>> index = 6;
>> obj.values = {};
>> f2 = @(t) postVolt(obj.values{preIdx}(index), obj.values{preIdx}(obj.voltIdx{postIdx}));
% Repeat above processing...
>> out
out =
@(t)-.05*obj.values{5}(6)*(obj.values{5}(obj.voltIdx{1})+80)