如何在MATLAB中处理函数参数的名称/值对


68

我有一个函数,将可选参数作为名称/值对。

function example(varargin)
% Lots of set up stuff
vargs = varargin;
nargs = length(vargs);
names = vargs(1:2:nargs);
values = vargs(2:2:nargs);

validnames = {'foo', 'bar', 'baz'};    
for name = names
   validatestring(name{:}, validnames);
end

% Do something ...
foo = strmatch('foo', names);
disp(values(foo))
end

example('foo', 1:10, 'bar', 'qwerty')

似乎在提取适当的值时需要付出很多努力(而且再次使用错误指定的输入仍然不是特别可靠)。有没有更好的方法来处理这些名称/值对?MATLAB附带有任何帮助程序功能吗?


Answers:


66

我更喜欢使用结构作为选项。这为您提供了存储选项和定义选项的简便方法。而且,整个事情变得相当紧凑。

function example(varargin)

%# define defaults at the beginning of the code so that you do not need to
%# scroll way down in case you want to change something or if the help is
%# incomplete
options = struct('firstparameter',1,'secondparameter',magic(3));

%# read the acceptable names
optionNames = fieldnames(options);

%# count arguments
nArgs = length(varargin);
if round(nArgs/2)~=nArgs/2
   error('EXAMPLE needs propertyName/propertyValue pairs')
end

for pair = reshape(varargin,2,[]) %# pair is {propName;propValue}
   inpName = lower(pair{1}); %# make case insensitive

   if any(strcmp(inpName,optionNames))
      %# overwrite options. If you want you can test for the right class here
      %# Also, if you find out that there is an option you keep getting wrong,
      %# you can use "if strcmp(inpName,'problemOption'),testMore,end"-statements
      options.(inpName) = pair{2};
   else
      error('%s is not a recognized parameter name',inpName)
   end
end

实际上,这有点可爱……我可能不得不开始使用该技巧了。
JudoWill 2010年

这种结构思想看起来很好地收拾了一切。我可能会尝试将其抽象为通用名称/值以构造函数。
Richie Cotton

在python中,所有这些都是内置的;)
jessexknight


13

我可能会花几个小时来解决这个问题,但是对于一般的Matlab签名处理,仍然没有很好的格式塔视图。但是这里有一些建议。

首先,采用自由放任的方法来验证输入类型。相信来电者。如果您确实需要强类型测试,则需要像Java这样的静态语言。尝试在Matlab中的每个地方强制执行类型安全,您将最终获得大部分的LOC和执行时间,专门用于在用户区中进行运行时类型测试和强制执行,这折衷了Matlab的强大功能和开发速度。我经过惨痛的教训才学到这个。

对于API签名(打算从其他函数而不是从命令行调用的函数),请考虑使用单个Args参数而不是varargin。然后,可以在多个参数之间传递它,而不必将其与来回用逗号分隔的varargin签名列表进行转换。就像乔纳斯(Jonas)所说的那样,结构非常方便。在结构和n-by-2 {name,value; ...}单元格之间也有很好的同构,您可以设置几个函数以在函数内部将它们之间转换为内部要使用的任何函数。

function example(args)
%EXAMPLE
%
% Where args is a struct or {name,val;...} cell array

无论您使用inputParser还是像其他其他出色示例一样滚动自己的名称/值解析器,都将其打包到一个单独的标准函数中,该函数将从具有名称/值签名的函数顶部调用。让它接受方便写出的数据结构中的默认值列表,并且您的arg解析调用看起来类似于函数签名声明,这有助于提高可读性并避免复制粘贴示例代码。

这就是解析调用的样子。

function out = my_example_function(varargin)
%MY_EXAMPLE_FUNCTION Example function 

% No type handling
args = parsemyargs(varargin, {
    'Stations'  {'ORD','SFO','LGA'}
    'Reading'   'Min Temp'
    'FromDate'  '1/1/2000'
    'ToDate'    today
    'Units'     'deg. C'
    });
fprintf('\nArgs:\n');
disp(args);

% With type handling
typed_args = parsemyargs(varargin, {
    'Stations'  {'ORD','SFO','LGA'}     'cellstr'
    'Reading'   'Min Temp'              []
    'FromDate'  '1/1/2000'              'datenum'
    'ToDate'    today                   'datenum'
    'Units'     'deg. C'                []
    });
fprintf('\nWith type handling:\n');
disp(typed_args);

% And now in your function body, you just reference stuff like
% args.Stations
% args.FromDate

这是一个以这种方式实现名称/值解析的函数。您可以将其挖空并将其替换为inputParser,您自己的类型约定等。考虑保留。在接收代码中,结构通常更方便处理,但是使用表达式和文字构造n×2单元更方便。(结构需要在每行连续加上“,...”,并防止单元格值扩展为非标量结构。)

function out = parsemyargs(args, defaults)
%PARSEMYARGS Arg parser helper
%
% out = parsemyargs(Args, Defaults)
%
% Parses name/value argument pairs.
%
% Args is what you pass your varargin in to. It may be
%
% ArgTypes is a list of argument names, default values, and optionally
% argument types for the inputs. It is an n-by-1, n-by-2 or n-by-3 cell in one
% of these forms forms:
%   { Name; ... }
%   { Name, DefaultValue; ... }
%   { Name, DefaultValue, Type; ... }
% You may also pass a struct, which is converted to the first form, or a
% cell row vector containing name/value pairs as 
%   { Name,DefaultValue, Name,DefaultValue,... }
% Row vectors are only supported because it's unambiguous when the 2-d form
% has at most 3 columns. If there were more columns possible, I think you'd
% have to require the 2-d form because 4-element long vectors would be
% ambiguous as to whether they were on record, or two records with two
% columns omitted.
%
% Returns struct.
%
% This is slow - don't use name/value signatures functions that will called
% in tight loops.

args = structify(args);
defaults = parse_defaults(defaults);

% You could normalize case if you want to. I recommend you don't; it's a runtime cost
% and just one more potential source of inconsistency.
%[args,defaults] = normalize_case_somehow(args, defaults);

out = merge_args(args, defaults);

%%
function out = parse_defaults(x)
%PARSE_DEFAULTS Parse the default arg spec structure
%
% Returns n-by-3 cellrec in form {Name,DefaultValue,Type;...}.

if isstruct(x)
    if ~isscalar(x)
        error('struct defaults must be scalar');
    end
    x = [fieldnames(s) struct2cell(s)];
end
if ~iscell(x)
    error('invalid defaults');
end

% Allow {name,val, name,val,...} row vectors
% Does not work for the general case of >3 columns in the 2-d form!
if size(x,1) == 1 && size(x,2) > 3
    x = reshape(x, [numel(x)/2 2]);
end

% Fill in omitted columns
if size(x,2) < 2
    x(:,2) = {[]}; % Make everything default to value []
end
if size(x,2) < 3
    x(:,3) = {[]}; % No default type conversion
end

out = x;

%%
function out = structify(x)
%STRUCTIFY Convert a struct or name/value list or record list to struct

if isempty(x)
    out = struct;
elseif iscell(x)
    % Cells can be {name,val;...} or {name,val,...}
    if (size(x,1) == 1) && size(x,2) > 2
        % Reshape {name,val, name,val, ... } list to {name,val; ... }
        x = reshape(x, [2 numel(x)/2]);
    end
    if size(x,2) ~= 2
        error('Invalid args: cells must be n-by-2 {name,val;...} or vector {name,val,...} list');
    end

    % Convert {name,val, name,val, ...} list to struct
    if ~iscellstr(x(:,1))
        error('Invalid names in name/val argument list');
    end
    % Little trick for building structs from name/vals
    % This protects cellstr arguments from expanding into nonscalar structs
    x(:,2) = num2cell(x(:,2)); 
    x = x';
    x = x(:);
    out = struct(x{:});
elseif isstruct(x)
    if ~isscalar(x)
        error('struct args must be scalar');
    end
    out = x;
end

%%
function out = merge_args(args, defaults)

out = structify(defaults(:,[1 2]));
% Apply user arguments
% You could normalize case if you wanted, but I avoid it because it's a
% runtime cost and one more chance for inconsistency.
names = fieldnames(args);
for i = 1:numel(names)
    out.(names{i}) = args.(names{i});
end
% Check and convert types
for i = 1:size(defaults,1)
    [name,defaultVal,type] = defaults{i,:};
    if ~isempty(type)
        out.(name) = needa(type, out.(name), type);
    end
end

%%
function out = needa(type, value, name)
%NEEDA Check that a value is of a given type, and convert if needed
%
% out = needa(type, value)

% HACK to support common 'pseudotypes' that aren't real Matlab types
switch type
    case 'cellstr'
        isThatType = iscellstr(value);
    case 'datenum'
        isThatType = isnumeric(value);
    otherwise
        isThatType = isa(value, type);
end

if isThatType
    out = value;
else
    % Here you can auto-convert if you're feeling brave. Assumes that the
    % conversion constructor form of all type names works.
    % Unfortunately this ends up with bad results if you try converting
    % between string and number (you get Unicode encoding/decoding). Use
    % at your discretion.
    % If you don't want to try autoconverting, just throw an error instead,
    % with:
    % error('Argument %s must be a %s; got a %s', name, type, class(value));
    try
        out = feval(type, value);
    catch err
        error('Failed converting argument %s from %s to %s: %s',...
            name, class(value), type, err.message);
    end
end

不幸的是,在Matlab中,字符串和datenum不是一流的类型。


1
@安德鲁:这里有一些好的建议。我同意所有这些样板代码都应该隐藏在一个函数中,并且您可能对输入检查不要太怪异,这是正确的。
Richie Cotton

6

我个人使用的自定义函数是从许多统计工具箱函数(例如kmeans,pca,svmtrain,ttest2等)使用的私有方法派生的。

作为内部实用程序功能,它在发行版中进行了更改和多次重命名。根据您的MATLAB版本,尝试查找以下文件之一:

%# old versions
which -all statgetargs
which -all internal.stats.getargs
which -all internal.stats.parseArgs

%# current one, as of R2014a
which -all statslib.internal.parseArgs

与任何未记录的函数一样,没有保证,可以在以后的版本中将其从MATLAB中删除,而无需任何通知...总之,我相信有人将它的旧版本发布为getargs。在File Exchange上。

该函数使用一组有效的参数名称及其默认值将它们作为名称/值对进行处理。它返回解析的参数作为单独的输出变量。默认情况下,无法识别的名称/值对会引发错误,但我们也可以在额外的输出中静默捕获它们。这是功能说明:

$MATLABROOT\toolbox\stats\stats\+internal\+stats\parseArgs.m

function varargout = parseArgs(pnames, dflts, varargin)
%
% [A,B,...] = parseArgs(PNAMES, DFLTS, 'NAME1',VAL1, 'NAME2',VAL2, ...)
%   PNAMES   : cell array of N valid parameter names.
%   DFLTS    : cell array of N default values for these parameters.
%   varargin : Remaining arguments as name/value pairs to be parsed.
%   [A,B,...]: N outputs assigned in the same order as the names in PNAMES.
%
% [A,B,...,SETFLAG] = parseArgs(...)
%   SETFLAG  : structure of N fields for each parameter, indicates whether
%              the value was parsed from input, or taken from the defaults.
%
% [A,B,...,SETFLAG,EXTRA] = parseArgs(...)
%   EXTRA    : cell array containing name/value parameters pairs not
%              specified in PNAMES.

例:

function my_plot(x, varargin)
    %# valid parameters, and their default values
    pnames = {'Color', 'LineWidth', 'LineStyle', 'Title'};
    dflts  = {    'r',           2,        '--',      []};

    %# parse function arguments
    [clr,lw,ls,txt] = internal.stats.parseArgs(pnames, dflts, varargin{:});

    %# use the processed values: clr, lw, ls, txt
    %# corresponding to the specified parameters
    %# ...
end

现在,可以使用以下任何一种方式来调用此示例函数:

>> my_plot(data)                                %# use the defaults
>> my_plot(data, 'linestyle','-', 'Color','b')  %# any order, case insensitive
>> my_plot(data, 'Col',[0.5 0.5 0.5])           %# partial name match

以下是一些无效的电话和引发的错误:

%# unrecognized parameter
>> my_plot(x, 'width',0)
Error using [...]
Invalid parameter name: width.

%# bad parameter
>> my_plot(x, 1,2)
Error using [...]
Parameter name must be text.

%# wrong number of arguments
>> my_plot(x, 'invalid')
Error using [...]
Wrong number of arguments.

%# ambiguous partial match
>> my_plot(x, 'line','-')
Error using [...]
Ambiguous parameter name: line.

inputParser:

正如其他人提到的那样,解析函数输入的官方推荐方法是使用inputParser类。它支持各种方案,例如指定必需的输入,可选的位置参数和名称/值参数。它还允许对输入执行验证(例如检查参数的类/类型和大小/形状)


@Amro:非常有趣,实际上,它与我自己滚动的解决方案非常相似。stackoverflow.com/questions/2775263/...
里奇棉

@OlegKomarov:感谢您的编辑。我更新了帖子以反映最新MATLAB版本中的更改,并演示实际的功能
Amro 2014年


5

MathWorks恢复了这匹老马,但具有非常有用的功能,可以直接满足此需求。它称为功能参数验证(一个人可以并且应该在文档中搜索的短语),并且带有发行版R2019b +。MathWorks也创建了有关它的视频。验证工作就像人们多年来提出的“技巧”一样。这是一个例子:

function ret = example( inputDir, proj, options )
%EXAMPLE An example.
% Do it like this.
% See THEOTHEREXAMPLE.

    arguments
        inputDir (1, :) char
        proj (1, 1) projector
        options.foo char {mustBeMember(options.foo, {'bar' 'baz'})} = 'bar'
        options.Angle (1, 1) {double, integer} = 45
        options.Plot (1, 1) logical = false
    end

    % Code always follows 'arguments' block.
    ret = [];
    switch options.foo
        case 'bar'
            ret = sind(options.Angle);
        case 'baz'
            ret = cosd(options.Angle);
    end
end

这是解包:

arguments块必须位于任何代码之前(帮助块之后为OK),并且必须遵循函数定义中定义的位置顺序,并且我相信每个参数都需要提及。必需的参数排在第一位,其次是可选参数,然后是名称-值对。MathWorks公司还建议不再使用的varargin关键字,但narginnargout仍然有用。

  • projector在这种情况下,类要求可以是自定义类,例如。
  • 必需的参数可能没有默认值。之所以知道是因为它们没有默认值。
  • 可选参数必须具有默认值。之所以知道是因为它们具有默认值。
  • 默认值必须能够通过相同的参数验证。换句话说,默认值为zeros(3)不会作为假定为字符向量的参数的默认值。
  • 名称/值对存储在optionsstruct中(提示我们可以使用struct传递关键字参数,就像kwargs在Python中一样)。
  • 很好的是,当您在函数调用中单击tab时,名称-值参数现在将显示为参数提示。

因此,在该示例中,inputDir是必需的参数,因为没有给出默认值。它还必须是1xN的字符向量。似乎与该语句矛盾,请注意,MATLAB将尝试转换提供的参数,以查看转换后的参数是否通过。例如,如果您通过12as inputDir,它将通过和inputDir == '12'。相反,zeros(3)它将不起作用,但是由于它是一个数组。当指定字符时,别忘了使字符串失败,反之亦然。MATLAB知道您想要什么并进行转换。您需要更深入地挖掘才能避免这种“灵活性”。

'foo'指定一个名称-值对,其值只能是'bar''baz'

MATLAB具有许多mustBe...验证功能(开始键入 mustBe并单击选项卡以查看可用功能),并且创建自己的功能非常容易。如果您创建自己的,则验证功能必须在输入不匹配时提供错误,例如与不同,例如,如果用户取消对话框uigetdir,则返回a 0。就个人而言,我遵循MATLAB的约定并调用我的验证函数 mustBe...,因此我拥有类似于mustBeNatural自然数的函数,并mustBeFile确保传递了一个实际存在的文件。

'Angle'指定一个名称-值对,其值必须为标量双精度或整数,因此,例如,example(pwd, 'foo', 'baz', 'Angle' [30 70])自从传递矢量以来将不起作用。

你明白了。该arguments模块具有很大的灵活性-我认为太多或太多-但对于简单功能,它既快速又容易。您可能仍然依赖于一个或多个inputParservalidateattributesassert,等解决更大验证的复杂性,但我总是试图东西东西变成一个arguments块,首先。如果变得难看,我将转到其他选项。


3

我更喜欢这样的自制样板代码:

function TestExample(req1, req2, varargin)
for i = 1:2:length(varargin)
    if strcmpi(varargin{i}, 'alphabet')
        ALPHA = varargin{i+1};

    elseif strcmpi(varargin{i}, 'cutoff')
        CUTOFF = varargin{i+1};
        %we need to remove these so seqlogo doesn't get confused
        rm_inds = [rm_inds i, i+1]; %#ok<*AGROW>

    elseif strcmpi(varargin{i}, 'colors')
        colors = varargin{i+1};
        rm_inds = [rm_inds i, i+1]; 
    elseif strcmpi(varargin{i}, 'axes_handle')
        handle = varargin{i+1};
        rm_inds = [rm_inds i, i+1]; 
    elseif strcmpi(varargin{i}, 'top-n')
        TOPN = varargin{i+1};
        rm_inds = [rm_inds i, i+1];
    elseif strcmpi(varargin{i}, 'inds')
        npos = varargin{i+1};
        rm_inds = [rm_inds i, i+1];
    elseif strcmpi(varargin{i}, 'letterfile')
        LETTERFILE = varargin{i+1};
        rm_inds = [rm_inds i, i+1];
    elseif strcmpi(varargin{i}, 'letterstruct')
        lo = varargin{i+1};
        rm_inds = [rm_inds i, i+1];
    end
end

这样,我可以模拟“选项”,值对,该对与大多数Matlab函数采用其参数的方式几乎相同。

希望能有所帮助,


2
@JudoWill:谢谢。使用switch语句而不是大量的ifelseif子句会更干净,并且else/otherwise子句对于捕获无法识别的输入会很有用。
Richie Cotton

+1是,对于简单的情况,我也绝对喜欢。switch虽然会很好。
zelanix 2014年

1

这是根据乔纳斯(Jonas)的想法尝试的解决方案。

function argStruct = NameValuePairToStruct(defaults, varargin)
%NAMEVALUEPAIRTOSTRUCT Converts name/value pairs to a struct.
% 
% ARGSTRUCT = NAMEVALUEPAIRTOSTRUCT(DEFAULTS, VARARGIN) converts
% name/value pairs to a struct, with defaults.  The function expects an
% even number of arguments to VARARGIN, alternating NAME then VALUE.
% (Each NAME should be a valid variable name.)
% 
% Examples: 
% 
% No defaults
% NameValuePairToStruct(struct, ...
%    'foo', 123, ...
%    'bar', 'qwerty', ...
%    'baz', magic(3))
% 
% With defaults
% NameValuePairToStruct( ...
%    struct('bar', 'dvorak', 'quux', eye(3)), ...
%    'foo', 123, ...
%    'bar', 'qwerty', ...
%    'baz', magic(3))
% 
% See also: inputParser

nArgs = length(varargin);
if rem(nArgs, 2) ~= 0
   error('NameValuePairToStruct:NotNameValuePairs', ...
      'Inputs were not name/value pairs');
end

argStruct = defaults;
for i = 1:2:nArgs
   name = varargin{i};
   if ~isvarname(name)
      error('NameValuePairToStruct:InvalidName', ...
         'A variable name was not valid');
   end
   argStruct = setfield(argStruct, name, varargin{i + 1});  %#ok<SFLD>
end

end

Thank's的解决方案也很有趣,但与Jonas的解决方案有点不同:您的方案接受任何参数名称,并只是检查它是否在语法上是有效的变量名称,但是Jonas的代码确实将参数名称限制为options中指定的名称。没有更好的代码,只是我认为弄清这种差异很重要。
令人惊讶的2014年

1

受到乔纳斯(Jonas)的回答的启发,但更为紧凑:

function example(varargin)
  defaults = struct('A',1, 'B',magic(3));  %define default values

  params = struct(varargin{:});
  for f = fieldnames(defaults)',
    if ~isfield(params, f{1}),
      params.(f{1}) = defaults.(f{1});
    end
  end

  %now just access them as params.A, params.B

1

有一个漂亮的功能叫做 parsepvpairs如果您可以访问MATLAB的财务工具箱,则可以很好地解决这一问题。它包含三个参数,期望的字段名称,默认字段值和收到的实际参数。

例如,这是一个在MATLAB中创建HTML图形并可以采用名为“ url”,“ html”和“ title”的可选字段值对的函数。

function htmldlg(varargin)
    names = {'url','html','title'};
    defaults = {[],[],'Padaco Help'};
    [url, html,titleStr] = parsepvpairs(names,defaults,varargin{:});

    %... code to create figure using the parsed input values
end

1

自古以来我就使用process_options.m。它稳定,易于使用,并已包含在各种matlab框架中。尽管对性能一无所知–可能是实现速度更快。

我最喜欢的功能process_optionsunused_args返回值,返回值可用于将输入args分成args组,例如用于子过程。

您可以轻松定义默认值。

最重要的是:使用process_options.m通常会导致可读性可维护性的选项定义。

示例代码:

function y = func(x, y, varargin)

    [u, v] = process_options(varargin,
                             'u', 0,
                             'v', 1);

0
function argtest(varargin)

a = 1;

for ii=1:length(varargin)/2
    [~] = evalc([varargin{2*ii-1} '=''' num2str(varargin{2*ii}) '''']);
end;

disp(a);
who

当然,这不会检查正确的分配,但是它很简单,任何无用的变量都将被忽略。它也仅适用于数字,字符串和数组,不适用于矩阵,单元格或结构。


0

我今天最后写了这篇,然后找到了这些提及。我的使用struct和struct'overlays'作为选项。它本质上反映了setstructfields()的功能,只是无法添加新参数。它还具有递归选项,而setstructfields()会自动进行递归。它可以通过调用struct(args {:})来获取成对值的单元格数组。

% Overlay default fields with input fields
% Good for option management
% Arguments
%   $opts - Default options
%   $optsIn - Input options
%       Can be struct(), cell of {name, value, ...}, or empty []
%   $recurseStructs - Applies optOverlay to any existing structs, given new
%   value is a struct too and both are 1x1 structs
% Output
%   $opts - Outputs with optsIn values overlayed
function [opts] = optOverlay(opts, optsIn, recurseStructs)
    if nargin < 3
        recurseStructs = false;
    end
    isValid = @(o) isstruct(o) && length(o) == 1;
    assert(isValid(opts), 'Existing options cannot be cell array');
    assert(isValid(optsIn), 'Input options cannot be cell array');
    if ~isempty(optsIn)
        if iscell(optsIn)
            optsIn = struct(optsIn{:});
        end
        assert(isstruct(optsIn));
        fields = fieldnames(optsIn);
        for i = 1:length(fields)
            field = fields{i};
            assert(isfield(opts, field), 'Field does not exist: %s', field);
            newValue = optsIn.(field);
            % Apply recursion
            if recurseStructs
                curValue = opts.(field);
                % Both values must be proper option structs
                if isValid(curValue) && isValid(newValue) 
                    newValue = optOverlay(curValue, newValue, true);
                end
            end
            opts.(field) = newValue;
        end
    end
end

我会说使用命名约定'defaults'和'new'可能会更好:P



0

我已经基于Jonas和Richie Cotton创建了一个函数。它实现了两种功能(灵活的参数或受限的,这意味着仅允许默认值中存在的变量),以及其他一些功能,例如语法糖和健全性检查。

function argStruct = getnargs(varargin, defaults, restrict_flag)
%GETNARGS Converts name/value pairs to a struct (this allows to process named optional arguments).
% 
% ARGSTRUCT = GETNARGS(VARARGIN, DEFAULTS, restrict_flag) converts
% name/value pairs to a struct, with defaults.  The function expects an
% even number of arguments in VARARGIN, alternating NAME then VALUE.
% (Each NAME should be a valid variable name and is case sensitive.)
% Also VARARGIN should be a cell, and defaults should be a struct().
% Optionally: you can set restrict_flag to true if you want that only arguments names specified in defaults be allowed. Also, if restrict_flag = 2, arguments that aren't in the defaults will just be ignored.
% After calling this function, you can access your arguments using: argstruct.your_argument_name
%
% Examples: 
%
% No defaults
% getnargs( {'foo', 123, 'bar', 'qwerty'} )
%
% With defaults
% getnargs( {'foo', 123, 'bar', 'qwerty'} , ...
%               struct('foo', 987, 'bar', magic(3)) )
%
% See also: inputParser
%
% Authors: Jonas, Richie Cotton and LRQ3000
%

    % Extract the arguments if it's inside a sub-struct (happens on Octave), because anyway it's impossible that the number of argument be 1 (you need at least a couple, thus two)
    if (numel(varargin) == 1)
        varargin = varargin{:};
    end

    % Sanity check: we need a multiple of couples, if we get an odd number of arguments then that's wrong (probably missing a value somewhere)
    nArgs = length(varargin);
    if rem(nArgs, 2) ~= 0
        error('NameValuePairToStruct:NotNameValuePairs', ...
            'Inputs were not name/value pairs');
    end

    % Sanity check: if defaults is not supplied, it's by default an empty struct
    if ~exist('defaults', 'var')
        defaults = struct;
    end
    if ~exist('restrict_flag', 'var')
        restrict_flag = false;
    end

    % Syntactic sugar: if defaults is also a cell instead of a struct, we convert it on-the-fly
    if iscell(defaults)
        defaults = struct(defaults{:});
    end

    optionNames = fieldnames(defaults); % extract all default arguments names (useful for restrict_flag)

    argStruct = defaults; % copy over the defaults: by default, all arguments will have the default value.After we will simply overwrite the defaults with the user specified values.
    for i = 1:2:nArgs % iterate over couples of argument/value
        varname = varargin{i}; % make case insensitive
        % check that the supplied name is a valid variable identifier (it does not check if the variable is allowed/declared in defaults, just that it's a possible variable name!)
        if ~isvarname(varname)
          error('NameValuePairToStruct:InvalidName', ...
             'A variable name was not valid: %s position %i', varname, i);
        % if options are restricted, check that the argument's name exists in the supplied defaults, else we throw an error. With this we can allow only a restricted range of arguments by specifying in the defaults.
        elseif restrict_flag && ~isempty(defaults) && ~any(strmatch(varname, optionNames))
            if restrict_flag ~= 2 % restrict_flag = 2 means that we just ignore this argument, else we show an error
                error('%s is not a recognized argument name', varname);
            end
        % else alright, we replace the default value for this argument with the user supplied one (or we create the variable if it wasn't in the defaults and there's no restrict_flag)
        else
            argStruct = setfield(argStruct, varname, varargin{i + 1});  %#ok<SFLD>
        end
    end

end

可以作为要点

对于有兴趣使用真实命名参数(使用类似于Python的语法,例如:myfunction(a = 1,b ='qwerty')的用户),请使用InputParser(仅适用于Matlab,Octave用户必须等到v4.2在至少,或者您可以尝试一个名为InputParser2的包装器。

另外,如果您不想总是输入argstruct.yourvar而直接使用yourvar,则可以作为奖励,也可以使用Jason S的以下代码片段

function varspull(s)
% Import variables in a structures into the local namespace/workspace
% eg: s = struct('foo', 1, 'bar', 'qwerty'); varspull(s); disp(foo); disp(bar);
% Will print: 1 and qwerty
% 
%
% Author: Jason S
%
    for n = fieldnames(s)'
        name = n{1};
        value = s.(name);
        assignin('caller',name,value);
    end
end
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.