【问题标题】:How do I set default values for functions parameters in MATLAB?如何在 MATLAB 中设置函数参数的默认值?
【发布时间】:2010-10-22 04:51:45
【问题描述】:

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.

【问题讨论】:

    标签: matlab arguments default


    【解决方案1】:

    我发现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)
    

    【讨论】:

    • 你能详细说明吗?
    • 我已粘贴到文档中。它显示了如何使用它来设置默认值的草图。
    【解决方案2】:

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

    在调用 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
    

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

    【讨论】:

    • 为什么不遵循 MATLAB 的名称-值对标准? wave(a,b,'flag',42,'fTrue',1)
    • 这当然也是一种选择,尤其是当参数很少时。但是,使用大量名称-值对调用 wave() 可能会降低代码的可读性。因此,我经常喜欢将某些输入分组到结构中。
    【解决方案3】:

    这是我为函数设置默认值的简单方法,使用“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
    

    【讨论】:

      【解决方案4】:

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

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

      (取自the documentation。)

      【讨论】:

        【解决方案5】:

        我很困惑,没有人指出 MATLAB 的开发人员之一 Loren 的 this blog post。该方法基于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 的整篇博文。我写了一个后续blog post 处理missing 位置默认值。我的意思是你可以这样写:

        somefun2Alt(a, b, '', 42)
        

        并且仍然有tol 参数的默认eps 值(当然还有func@magic 回调)。 Loren 的代码只需稍加修改即可实现这一点。

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

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

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

        【讨论】:

        • 您后续博文的链接已损坏;你能修好吗?
        • 这个答案需要更多的可见性。我记得 Loren 的博客文章,并且正在寻找它,并打开了这个 stackoverflow 问题,希望我能以这种方式轻松找到它。
        【解决方案6】:

        Matlab 没有为此提供机制,但您可以在用户空间代码中构造一个比 inputParser 或“if nargin

        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”全部大写。我知道这是它的官方样式,但这似乎是在对我大喊大叫,所以我不会在自己的写作中使用它。
        【解决方案7】:

        这是一个处理这个问题的好方法,只占用三行代码(除非换行)。以下内容是直接从我正在编写的函数中提取的,它似乎可以按预期工作:

        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
        

        【讨论】:

          【解决方案8】:

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

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

          这样我们就可以使用

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

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

          【讨论】:

            【解决方案9】:
            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。

            【讨论】:

              【解决方案10】:

              您可能想在 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;
              

              【讨论】:

                【解决方案11】:

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

                【讨论】:

                • 据我所知,this 是首选方法。它干净、自记录(更多的是一堆if nargin 声明)、易于维护、紧凑且灵活。
                【解决方案12】:

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

                【讨论】:

                  【解决方案13】:

                  这或多或少是从MATLAB manual中提升的;我只有过客经验...

                  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
                  

                  【讨论】:

                  • 我更正的代码中有几个错误。首先,需要定义“optargin”。其次,“varargin”是一个收集所有后续输入的元胞数组,因此您必须使用元胞数组索引从中删除值。
                  • 我需要检查我的视力;我发誓我昨天在手册中没有看到任何内容:(
                  • @kyle:别担心,我们都会犯错。这就是为什么我喜欢 SO 的 wiki-ish 风格:如果我犯了一些愚蠢的错字,通常会有其他人可以抓住它并为我快速修复它。 =)
                  【解决方案14】:

                  没有像您尝试过的那样直接的方法。

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

                  function f(arg1, arg2, arg3)
                  
                    if nargin < 3
                      arg3 =   'some default'
                    end
                  
                  end
                  

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

                  您可能会看看vararginnargchk 等。它们对于这类事情很有用。 varargs 允许您保留可变数量的最终参数,但这并不能解决部分/全部参数的默认值问题。

                  【讨论】:

                    【解决方案15】:

                    在了解ASSIGNIN(感谢b3this answer)和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 ')']) ;
                    

                    【讨论】:

                      【解决方案16】:

                      另一种不那么老套的方法是

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

                      【讨论】:

                      • 如果要使用 MATLAB Coder 生成 C 代码,此选项不起作用,因为 Coder 不支持“exist”函数。
                      猜你喜欢
                      • 1970-01-01
                      • 2013-01-01
                      • 2019-11-02
                      • 1970-01-01
                      • 2010-09-26
                      • 2010-10-28
                      • 2017-12-21
                      相关资源
                      最近更新 更多