如何在Matlab中为函数参数设置默认值?


127

Matlab中可以有默认参数吗?例如,在这里:

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

我希望真正的解决方案是wave函数的可选参数。如果有可能,谁能证明这样做的正确方法?目前,我正在尝试上面发布的内容,然后得到:

??? 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.

Answers:


151

没有像您尝试的那样直接执行此操作的方法。

通常的方法是使用“ varargs”并对照参数数量进行检查。就像是:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

您可以使用一些更高级的功能isempty,等等,并且您可能想看看Matlab中心,找到一些捆绑了这些东西的软件包。

你可能有一个看看vararginnargchk等他们是有用的这样的事情功能。varargs允许您保留可变数量的最终参数,但这并不能解决某些/全部默认值的问题。


58

我使用该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函数,因此将验证器留空。


7
据我所知,是首选方法。它是干净的,自记录的(更像是一堆政治家if nargin),易于维护,紧凑且灵活。
JnBrymn 2011年

19

另一种较不hacky的方式是

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

如果您要使用MATLAB Coder生成C代码,则此选项不起作用,因为Coder不支持“ exist”功能。
Dave Tillman

10

是的,有能力像你写的那样做真的很好。但是在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中传递它们的一种非常好的方法。


7

这是我使用“ 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

问候!



3

尽管有时可能会从matlab中将其删除,但也可以使用“ hack”:函数eval实际上接受两个参数,如果第一个发生错误,则运行第二个参数。

因此我们可以使用

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

将值1用作参数的默认值


3

我相信我发现了一种解决此问题的好方法,仅占用三行代码(禁止换行)。以下内容直接从我正在编写的函数中删除,似乎可以按需工作:

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

只是以为我会分享。


3

我很困惑,没有人指出Matlab开发人员之一Loren的这篇博客文章。该方法基于varargin并避免了所有这些无休止的痛苦if-then-elseswitch情况复杂的情况。当有几个默认值时,效果非常显着。这是链接博客中的一个示例:

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

% only want 3 optional inputs at most
numvarargs = length(varargin);
if numvarargs > 3
    error('myfuns:somefun2Alt:TooManyInputs', ...
        'requires at most 3 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)

仍然有默认eps的值tol参数(和@magic回调func当然)。洛伦(Loren)的代码对此进行了细微但棘手的修改。

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

  1. 即使有很多默认值,样板代码也不会变得庞大(与if-then-else方法系列相反,方法系列的每个新默认值都会更长)
  2. 所有默认值都放在一个位置。如果其中任何一项需要更改,那么您只需看看一个地方。

Trooth被告知,也有一个缺点。当您在Matlab shell中键入函数而忘记其参数时,您会看到无用varargin的提示。为了解决这个问题,建议您编写一个有意义的用法子句。


您的后续博客文章的链接已断开;你能修好它吗?
equaeghe

2

知悉后ASSIGNIN(感谢这个答案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 ')']) ;

1

这在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

1
我更正的代码中有几个错误。首先,需要定义“ optargin”。其次,“ varargin”是一个收集所有后续输入的单元格数组,因此您必须使用单元格索引来从中删除值。
gnovice

我需要检查一下视力;我发誓昨天在手册中什么都没看到:(
kyle 2009年

@kyle:不用担心,我们都会犯错。这就是为什么我喜欢SO的Wiki风格:如果我做一些愚蠢的错字,通常周围会有其他人可以抓住它并为我快速修复它。=)
gnovice

1

Matlab对此不提供任何机制,但是您可以在用户区代码中构造一个比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函数。但这很方便。


0

您可能要parseparams在matlab中使用该命令;用法如下所示:

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;

0
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。


0

如果您要使用八度音,您可以这样做-但可惜的是matlab不支持这种可能性

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

(摘自doc


0

我喜欢以一种更加面向对象的方式进行操作。在调用wave()之前,将一些参数保存在结构中,例如。一种叫参数:

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

在wave函数中,然后检查struct参数是否包含一个名为“ flag”的字段,如果是,则其值不为空。然后,为它赋八位数一个您之前定义的默认值,或者为参数struct中的参数指定的值:

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

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


为什么不遵循MATLAB名称/值对标准?wave(a,b,'flag',42,'fTrue',1)
克里斯·伦戈

当然,这也是一种选择,尤其是当参数只有很少的时候。但是,使用大量名称/值对调用wave()可能会降低代码的可读性。因此,我经常喜欢将某些输入分组为结构。
CheshireCat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.