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

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

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 для помощи?

Ответы [ 13 ]

0 голосов
/ 10 июня 2014

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

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
0 голосов
/ 11 сентября 2011

Сегодня я написал это, а потом нашел эти упоминания. Мой использует 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

Я бы сказал, что было бы лучше использовать соглашение об именах «по умолчанию» и «новый»:

0 голосов
/ 11 августа 2010
function argtest(varargin)

a = 1;

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

disp(a);
who

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

...