131

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

16 回答 16

157

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

通常的方法是使用“可变参数”并检查参数的数量。就像是:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

您可以使用isempty等做一些更有趣的事情,并且您可能想查看 MATLAB central 以获得一些捆绑这些东西的包。

您可能会看一下varargin,nargchk等。它们是这类事情的有用功能。可变参数允许您保留可变数量的最终参数,但这并不能解决部分/全部参数的默认值问题。

于 2009-04-28T00:55:17.977 回答
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函数,所以我将验证器留空。

于 2009-05-02T12:37:40.127 回答
19

另一个稍微不那么老套的方法是

function output = fun(input)
   if ~exist('input','var'), input='BlahBlahBlah'; end
   ...
end
于 2011-02-20T15:21:27.967 回答
11

是的,拥有按照您所写的那样做的能力可能真的很好。但这在 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 中传递它们的一种非常好的方法。

于 2009-04-28T11:56:57.280 回答
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
于 2015-06-09T14:01:02.400 回答
6

我很困惑没有人指出MATLAB 的开发人员之一 Loren 的这篇博文。该方法基于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)

并且仍然具有参数的默认epstol(当然还有@magic回调func)。Loren 的代码通过轻微但棘手的修改来实现这一点。

最后,只是这种方法的几个优点:

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

说实话,也有缺点。当您在 MATLAB shell 中键入函数并忘记其参数时,您会看到一个无用varargin的提示。为了解决这个问题,建议您编写一个有意义的使用条款。

于 2014-04-06T19:01:35.290 回答
3

我发现parseArgs函数非常有用。

这是它的文档:

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

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

  • ArgStruct是一个充满了具有默认值的命名参数的结构。
  • Flagtype 参数是不需要值的参数。(如果存在,该值将设置为 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)
于 2009-04-28T21:00:52.633 回答
3

还有一个可以使用的“hack”,尽管它可能会在某些时候从 MATLAB 中删除:

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

因此我们可以使用

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

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

于 2010-12-07T09:30:38.933 回答
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
于 2014-01-16T14:18:21.187 回答
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 ')']) ;
于 2011-03-22T09:55:50.437 回答
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
于 2009-04-28T00:59:25.207 回答
1

Matlab 没有为此提供一种机制,但是您可以在用户代码中构造一个比 inputParser 或“如果 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 函数。但这很方便。

于 2009-05-13T04:18:36.217 回答
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;
于 2010-10-08T19:04:52.863 回答
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。

于 2010-10-11T13:56:52.427 回答
0

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

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

(取自文档。)

于 2014-06-10T14:09:18.040 回答
0

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

在调用 wave() 之前,将一些参数保存在一个结构中,例如一个称为参数的参数:

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

然后在 wave 函数中检查结构参数是否包含名为“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

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

于 2018-05-24T12:04:16.337 回答