如何在 MATLAB 中设置函数参数的默认值?

有没有可能在 MATLAB 中有默认的参数?

例如,这里:

function wave(a, b, n, k, T, f, flag, fTrue=inline('0'))

我希望真解是波函数的一个可选参数。如果可能的话,做这件事的正确方法是什么?

目前,我正在尝试上面发布的内容,我得到了:

??? Error: File: wave.m Line: 1 Column: 37
The expression to the left of the equals sign is not a valid target for an assignment.
122155 次浏览

没有你尝试过的直接方法。

通常的方法是使用“ varargs”并检查参数的数量,比如:

function f(arg1, arg2, arg3)


if nargin < 3
arg3 =   'some default'
end


end

你可以用 isempty来做一些更有趣的事情,你可能想在 MATLAB 中找到一些捆绑这些事情的软件包。

你可以看看 vararginnargchk等等。它们是这类事情的有用函数。Varargs允许您保留数量可变的最终参数,但是这并不能解决某些/所有参数的默认值问题。

这或多或少是从 MATLAB 手册中提取出来的; 我只有过去的经验..。

function my_output = wave ( a, b, n, k, T, f, flag, varargin )
optargin = numel(varargin);
fTrue = inline('0');
if optargin > 0
fTrue = varargin{1};
end
% code ...
end

是的,如果能够像您所写的那样做,那可能会非常好。但这在 MATLAB 中是不可能的。我的许多允许参数默认值的实用程序往往在开始时使用明确的检查来编写,如下所示:

if (nargin<3) or isempty(myParameterName)
MyParameterName = defaultValue;
elseif (.... tests for non-validity of the value actually provided ...)
error('The sky is falling!')
end

好的,所以我通常会应用一个更好、更具描述性的错误消息。请注意,检查空变量允许用户传入一对空括号 []作为变量的占位符,该变量将采用其默认值。但是,作者仍然必须提供代码来用默认值替换该空参数。

我的实用程序比较复杂,它们使用 很多参数(所有这些参数都有默认参数) ,通常会使用属性/值对接口作为默认参数。这个基本范例可以在 MATLAB 的句柄图形工具中看到,也可以在 Optimset、 odeset 等中看到。

作为使用这些属性/值对的一种方法,您需要了解 Varargin,将它作为一种向函数输入完全可变数量的参数的方法。我编写(并发布)了一个实用程序来处理这样的属性/值对 Parse _ pv _ pairs. m。它可以帮助您将属性/值对转换为 MATLAB 结构。它还允许您为每个参数提供默认值。将一个笨拙的参数列表转换为一个结构是在 MATLAB 中传递它们的一个 非常不错的方法。

我发现 ParseArgs函数非常有用。

这是它的文档:

用于解析 varargin的辅助函数。使得编写带有如下参数的函数变得容易: subaxis(4,2,1,'spacing',0,'marginleft',.1,'H','pt',.1)

ArgStruct=parseArgs(varargin,ArgStruct[,FlagtypeParams[,Aliases]])

  • ArgStruct的结构中充满了具有默认值的命名参数。
  • Flagtype params 是指不需要值的参数(如果存在该值,该值将被设置为1)
  • 别名可用于将一个参数-名称映射到多个 argstruct 字段

例子用法:

function parseargtest(varargin)


%define the acceptable named arguments and assign default values
Args=struct('Holdaxis',0, ...
'SpacingVertical',0.05,'SpacingHorizontal',0.05, ...
'PaddingLeft',0,'PaddingRight',0,'PaddingTop',0,'PaddingBottom',0, ...
'MarginLeft',.1,'MarginRight',.1,'MarginTop',.1,'MarginBottom',.1, ...
'rows',[],'cols',[]);


%The capital letters define abrreviations.
% Eg. parseargtest('spacingvertical',0) is equivalent to parseargtest('sv',0)


Args=parseArgs(varargin,Args, ...
% fill the arg-struct with values entered by the user, e.g.
% {'Holdaxis'}, ... %this argument has no value (flag-type)
% {'Spacing' {'sh','sv'}; 'Padding' {'pl','pr','pt','pb'}; 'Margin' {'ml','mr','mt','mb'}});


disp(Args)

我使用了 inputParser对象来处理设置默认选项。MATLAB 不会接受您在问题中指定的类 Python 格式,但是您应该能够像这样调用函数:

wave(a, b, n, k, T, f, flag, 'fTrue', inline('0'))

定义 wave函数之后,如下所示:

function wave(a, b, n, k, T, f, flag, varargin)


i_p = inputParser;
i_p.FunctionName = 'WAVE';


i_p.addRequired('a', @isnumeric);
i_p.addRequired('b', @isnumeric);
i_p.addRequired('n', @isnumeric);
i_p.addRequired('k', @isnumeric);
i_p.addRequired('T', @isnumeric);
i_p.addRequired('f', @isnumeric);
i_p.addRequired('flag', @isnumeric);
i_p.addOptional('ftrue', inline('0'), 1);


i_p.parse(a, b, n, k, T, f, flag, varargin{:});

现在传递给函数的值可以通过 i_p.Results获得。另外,我不确定如何验证为 ftrue传入的参数实际上是 inline函数,所以我将验证器留空。

Matlab 没有为此提供机制,但是您可以在 userland 代码中构造一个比 inputParser 或“ if nargin < 1...”序列更简洁的机制。

function varargout = getargs(args, defaults)
%GETARGS Parse function arguments, with defaults
%
% args is varargin from the caller. By convention, a [] means "use default".
% defaults (optional) is a cell vector of corresponding default values


if nargin < 2;  defaults = {}; end


varargout = cell(1, nargout);
for i = 1:nargout
if numel(args) >= i && ~isequal(args{i}, [])
varargout{i} = args{i};
elseif numel(defaults) >= i
varargout{i} = defaults{i};
end
end

然后你可以在函数中这样调用它:

function y = foo(varargin)
%FOO
%
% y = foo(a, b, c, d, e, f, g)


[a, b,  c,       d, e, f, g] = getargs(varargin,...
{1, 14, 'dfltc'});

格式设置是一种约定,允许您从参数名称向下读取它们的默认值。可以使用可选的参数类型规范(用于错误检测或隐式转换)和参数计数范围扩展 getargs ()。

这种方法有两个缺点。首先,它很慢,所以你不想用它来处理循环中调用的函数。其次,Matlab 的函数帮助——命令行上的自动完成提示——不适用于 varargin 函数。但这很方便。

您可能希望在 MATLAB 中使用 parseparams命令; 其用法如下:

function output = wave(varargin);
% comments, etc
[reg, props] = parseparams(varargin);
ctrls = cell2struct(props(2:2:end),props(1:2:end),2);  %yes this is ugly!
a = reg{1};
b = reg{2};
%etc
fTrue = ctrl.fTrue;
function f(arg1, arg2, varargin)


arg3 = default3;
arg4 = default4;
% etc.


for ii = 1:length(varargin)/2
if ~exist(varargin{2*ii-1})
error(['unknown parameter: ' varargin{2*ii-1}]);
end;
eval([varargin{2*ii-1} '=' varargin{2*ii}]);
end;

例如,f(2,4,'c',3)使参数 c为3。

也有一个“黑客”,可以使用,虽然它可能会从 MATLAB 删除在某些点:

函数 Eval实际上接受两个参数,如果第一个参数发生错误,则运行第二个参数。

因此我们可以利用

function output = fun(input)
eval('input;', 'input = 1;');
...
end

使用值1作为参数的默认值。

Another slightly less hacky way is

function output = fun(input)
if ~exist('input','var'), input='BlahBlahBlah'; end
...
end

在了解了 指派(感谢 这个答案 by B3)和 EVALIN之后,我编写了两个函数来最终获得一个非常简单的调用结构:

setParameterDefault('fTrue', inline('0'));

这是清单:

function setParameterDefault(pname, defval)
% setParameterDefault(pname, defval)
% Author: Tobias Kienzler (https://stackoverflow.com/users/321973)
% sets the parameter NAMED pname to the value defval if it is undefined or
% empty


if ~isParameterDefined('pname')
error('paramDef:noPname', 'No parameter name defined!');
elseif ~isvarname(pname)
error('paramDef:pnameNotChar', 'pname is not a valid varname!');
elseif ~isParameterDefined('defval')
error('paramDef:noDefval', ['No default value for ' pname ' defined!']);
end;


% isParameterNotDefined copy&pasted since evalin can't handle caller's
% caller...
if ~evalin('caller',  ['exist(''' pname ''', ''var'') && ~isempty(' pname ')'])
callername = evalin('caller', 'mfilename');
warnMsg = ['Setting ' pname ' to default value'];
if isscalar(defval) || ischar(defval) || isvector(defval)
warnMsg = [warnMsg ' (' num2str(defval) ')'];
end;
warnMsg = [warnMsg '!'];
warning([callername ':paramDef:assigning'], warnMsg);
assignin('caller', pname, defval);
end

还有

function b = isParameterDefined(pname)
% b = isParameterDefined(pname)
% Author: Tobias Kienzler (https://stackoverflow.com/users/321973)
% returns true if a parameter NAMED pname exists in the caller's workspace
% and if it is not empty


b = evalin('caller',  ['exist(''' pname ''', ''var'') && ~isempty(' pname ')']) ;

这里有一个巧妙的方法来处理这个问题,只占用三行代码(除了换行)。以下内容直接取自我正在编写的一个函数,并且似乎按照预期工作:

defaults = {50/6,3,true,false,[375,20,50,0]}; % Set all defaults
defaults(1:nargin-numberForcedParameters) = varargin; % Overload with function input
[sigma,shifts,applyDifference,loop,weights] = ...
defaults{:}; % Unfold the cell struct

我感到困惑的是,没有人指出由罗兰,MATLAB 的开发人员之一的 这篇博文。该方法以 varargin为基础,避免了所有那些无休止的、痛苦的 if-then-elseswitch病例伴有复杂的条件。当有 有几个默认值时,效果是 戏剧性。下面是一个来自博客链接的例子:

function y = somefun2Alt(a, b, varargin)
% Some function that requires two inputs and has some optional inputs.


% Only want three optional inputs at most
numvarargs = length(varargin);
if numvarargs > 3
error('myfuns:somefun2Alt:TooManyInputs', ...
'requires at most three optional inputs');
end


% Set defaults for optional inputs
optargs = {eps 17 @magic};


% Now put these defaults into the valuesToUse cell array,
% and overwrite the ones specified in varargin.
optargs(1:numvarargs) = varargin;
% or ...
% [optargs{1:numvarargs}] = varargin{:};


% Place optional args in memorable variable names
[tol, mynum, func] = optargs{:};

如果你还是不明白,那么试着阅读 Loren 的整篇博文。我已经写了一个后续的 博客文章处理 失踪了位置默认值。我的意思是你可以这样写:

somefun2Alt(a, b, '', 42)

并且仍然有 tol参数的默认 eps值(当然还有 func@magic回调)。Loren 的代码允许这样做,只是做了一些微小但棘手的修改。

最后,这种方法的一些优点是:

  1. 即使有很多默认值,样板代码也不会变得很大(与 if-then-else方法家族相反,if-then-else方法随着每个新的默认值变得更长)
  2. 所有的默认值都在一个地方。如果这些需要改变,你只有一个地方可以看。

说实话,这也有不利的一面。当您在 MATLAB shell 中键入函数并忘记它的参数时,您将看到一个无用的 varargin作为提示。为了解决这个问题,建议你写一个有意义的用法条款。

如果你想使用 八度,你可以这样做-但遗憾的是 MATLAB 不支持这种可能性:

function hello (who = "World")
printf ("Hello, %s!\n", who);
endfunction

(摘自 文件)

这是我使用“ try”将默认值设置为函数的简单方法:

function z = myfun (a, varargin)


%% Default values
b = 1;
c = 1;
d = 1;
e = 1;


try
b = varargin{1};
c = varargin{2};
d = varargin{3};
e = varargin{4};
end


%% Calculation
z = a * b * c * d * e;
end

我喜欢以一种更加面向对象的方式来做这件事。

在调用 wave ()之前,将一些参数保存在一个 struct 中,例如一个名为 Parameter 的参数:

parameters.flag = 42;
parameters.fTrue = 1;
wave(a, b, n, k, T, f, parameters);

然后在 wave 函数中检查 struct 参数是否包含名为“ Flag”的字段,如果包含,则检查其值是否为非空。然后,为它分配一个之前定义的默认值,或者作为参数结构中的参数给出的值:

function output = wave(a, b, n, k, T, f, parameters)
flagDefault = 18;
fTrueDefault = 0;
if (isfield(parameters,'flag') == 0 || isempty(parameters.flag)), flag=flagDefault;else flag=parameters.flag; end
if (isfield(parameter, 'fTrue') == 0 || isempty(parameters.fTrue)), fTrue=fTrueDefault;else fTrue=parameters.fTrue; end
...
end

这使得处理大量参数变得更加容易,因为它不依赖于给定参数的顺序。也就是说,如果以后需要添加更多的参数,这也很有帮助,因为您不必为此更改函数签名。

2019b 年,arguments引入了“ True”默认参数(即通过特定构建的语言特性而不是解析函数或手工编写的代码实现的默认参数)。

作为一个简化的例子,考虑以下函数:

function myFun(a,b,c)
arguments
a
b
c = 0
end
disp([a b c])
end

c是一个默认值为0的可选输入。输入 ab没有定义默认值,因此需要输入。

现在可以使用或不使用传递 c 的值来调用 myFun,如果没有提供值,则使用默认值 c=0

>> myFun(1,2)
1     2     0


>> myFun(1,2,3)
1     2     3