Как работать с парами имя / значение аргументов функции в MATLAB

53

У меня есть функция, которая принимает необязательные аргументы как пары имя/значение.

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, чтобы помочь?

Теги:
function
arguments
function-parameter

13 ответов

47
Лучший ответ

Я предпочитаю использовать структуры для своих опций. Это дает вам простой способ хранения параметров и простой способ их определения. Кроме того, все это становится довольно компактным.

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
  • 0
    @gnovice: Спасибо!
  • 0
    на самом деле, это довольно мило ... Возможно, мне придется начать использовать этот трюк.
Показать ещё 1 комментарий
39

InputParser помогает с этим. Подробнее см. Входы параметров анализа.

  • 1
    @Matthew: Это, вероятно, лучший встроенный способ борьбы с вещами. Я скорее пристрастился к идее Джонаса о структуре опционов, но это уже второе.
  • 1
    Это лучший ответ, поскольку он соответствует собственному стилю математики и лучшим практикам.
Показать ещё 1 комментарий
11

Я мог бы часами прокручивать это, но у меня нет хорошего гештальт-представления общей обработки подписи Matlab. Но здесь несколько советов.

Сначала возьмите подход laissez faire для проверки типов ввода. Доверяйте вызывающему абоненту. Если вам действительно требуется сильное тестирование типа, вам нужен статический язык, такой как Java. Попытайтесь обеспечить безопасность типов везде, где находится в Matlab, и в итоге вы получите значительную часть вашего LOC и время выполнения, предназначенное для тестирования времени выполнения и принуждения в userland, которое торгует большой скоростью и скоростью разработки Matlab, Я усвоил этот трудный путь.

Для подписей API (функции, предназначенные для вызова из других функций, а не из командной строки), рассмотрите возможность использования одного аргумента Args вместо varargin. Затем он может быть передан между несколькими аргументами без необходимости его преобразования в и из списка, разделенного запятыми, для подписей varargin. Структуры, как говорит Джонас, очень удобны. Также существует хороший изоморфизм между структурами и ячейками n-by-2 {name, value;...}, и вы можете настроить пару функций для преобразования между ними внутри ваших функций в зависимости от того, что он хочет использовать внутри.

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

Независимо от того, используете ли вы входной парсер или сворачиваете свой собственный парсер имен/валов, как эти другие прекрасные примеры, упакуйте его в отдельную стандартную функцию, которую вы вызовете из верхней части своих функций, имеющих подписи имени/знака. Принять ли он список значений по умолчанию в структуре данных, который удобно записывать, а ваши вызовы для анализа аргументов будут выглядеть как декларации подписи функций, что упрощает чтение и избегает шаблонов шаблонов копирования и вставки.

Здесь будут выглядеть вызовы разбора.

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

И вот функция для реализации парсинга name/val таким образом. Вы могли бы выпустить его и заменить его на входной парсер, свои собственные соглашения типа и т.д. Я думаю, что соглашение о ячейке n-by-2 делает для хорошо читаемого исходного кода; подумайте об этом. Структуры, как правило, более удобны для обработки в коде приема, но ячейки n-by-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 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 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 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.

  • 1
    @ Эндрю: Несколько полезных советов. Я согласен с тем, что весь этот шаблонный код должен быть скрыт в функции, и вы, вероятно, правы в том, что не слишком надумали контроль при проверке ввода.
5

Лично я использую настраиваемую функцию, полученную из частного метода, используемого многими функциями Статистического инструмента (например, 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 в обмене файлами.

Функция обрабатывает параметры как пары имя/значение, используя набор допустимых имен параметров вместе с их значениями по умолчанию. Он возвращает анализируемые параметры как отдельные выходные переменные. По умолчанию непризнанные пары имя/значение вызывают ошибку, но мы также можем без помех их записывать в дополнительный вывод. Вот описание функции:

$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. Он поддерживает различные схемы, такие как указание необходимых входов, необязательные позиционные аргументы и параметры имени/значения. Он также позволяет выполнять validation на входах (например, проверять класс/тип и размер/форму аргументов)

  • 0
    @Amro: Очень интересно, и на самом деле, очень похоже на решение, которое я только что выполнил. stackoverflow.com/questions/2775263/...
  • 0
    @OlegKomarov: спасибо за редактирование. Я обновил пост, чтобы отразить изменения в последней версии MATLAB, а также чтобы показать функцию в действии
5

Прочитайте информационный пост Лорен по этому вопросу. Не забудьте прочитать раздел комментариев... - Вы увидите, что существует довольно много разных подходов к этой теме. Все они работают, поэтому выбор предпочтительного метода действительно зависит от личного вкуса и ремонтопригодности.

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

Таким образом, я смогу имитировать пара параметров "option", которая почти идентична тем, как большинство функций Matlab принимают свои аргументы.

Надеюсь, что это поможет,

Воля

  • 2
    @JudoWill: Спасибо. Это было бы чище с switch заявлением , а не много , если elseif статей, а else / в otherwise положение было бы хорошо для захвата непризнанных входов.
  • 0
    +1 Да, для простых случаев я определенно предпочитаю это тоже. switch будет хорошо, хотя.
1

Вдохновленный ответом Джонаса, но более компактный:

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

Здесь решение, которое я тестирую, основывается на идее Джонаса.

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
  • 0
    Спасибо, ваше решение также очень интересно, но оно немного отличается от решения Джонаса: ваше принимает любое имя аргумента и просто проверяет, является ли оно синтаксически допустимым именем переменной, но код Джонаса ограничивает имена аргументов теми, которые указаны в опциях. Нет лучшего кода, просто я думаю, что важно прояснить эту разницу.
0

Существует отличная функция под названием parsepvpairs, которая прекрасно позаботится об этом, если у вас есть доступ к инструментарию для финансов MATLAB. Он принимает три аргумента, ожидаемые имена полей, значения полей по умолчанию и фактические полученные аргументы.

Например, здесь функция, которая создает фигуру HTML в MATLAB и может принимать необязательные пары значений полей с именем "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
0

С возрастом я использую process_options.m. Он стабилен, прост в использовании и включен в различные системы Matlab. Не знаю ничего о производительности, хотя – возможно, что существуют более быстрые реализации.

Функция, которая больше всего нравится с process_options, - это возвращаемое значение unused_args, которое может использоваться для разделения входных аргументов в группах аргументов для, например, подпроцессов.

И вы можете легко определить значения по умолчанию.

Еще одна хорошая вещь: использование process_options.m обычно приводит к читаемым и поддерживаемым опциям.

0

Я сделал функцию, основанную на Джонасе и Ричи Коттоне. Он реализует обе функциональности (гибкие аргументы или ограничения, что означает, что допускаются только переменные, существующие в значениях по умолчанию), а также несколько других вещей, таких как синтаксический контроль сахара и проверки работоспособности.

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 inside a sub-struct (happens on Octave), because anyway it 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 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 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 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 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 no restrict_flag)
        else
            argStruct = setfield(argStruct, varname, varargin{i + 1});  %#ok<SFLD>
        end
    end

end

Также доступен как Gist.

И для тех, кто интересуется настоящими именованными аргументами (с синтаксисом, похожим на 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
0

В итоге я написал это сегодня, а затем нашел эти упоминания. Mine использует 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
    Кроме того, я смог разработать базовую перестановку для проведения полнофакторных экспериментов с заданными параметрами наложения:
  • 0
    pastebin.com/qpW4BUFs
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

Это, конечно, не проверяет правильные назначения, но это просто и любая бесполезная переменная будут проигнорированы в любом случае. Он также работает только для чисел, строк и массивов, но не для матриц, ячеек или структур.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню