Как установить значения по умолчанию для параметров функций в Matlab?


возможно ли иметь аргументы по умолчанию в 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.
16 112

16 ответов:

нет прямого способа сделать это, как вы пытались.

обычный подход-использовать "varargs" и проверять количество аргументов. Что-то вроде:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

есть несколько более тонких вещей, которые вы можете сделать с isempty и т. д., и вы можете посмотреть на Matlab central для некоторых пакетов, которые связывают такие вещи.

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

я использовал inputParser объект для работы с настройкой параметров по умолчанию. Matlab не примет python-подобный формат, указанный в вопросе, но вы должны быть в состоянии вызвать функцию следующим образом:

wave(a,b,n,k,T,f,flag,'fTrue',inline('0'))

после определения такой:

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 функция поэтому оставила валидатор пустым.

еще один немного менее хакерский способ -

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

Да, было бы очень приятно иметь возможность сделать как вы написали. Но это невозможно в 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

хорошо, поэтому я обычно применяю лучшее, более описательное сообщение об ошибке. Смотрите, что проверка пустой переменной позволяет пользователю передать пустую пару скобок, [], в качестве заполнителя для переменной, которая будет принимать его значение по умолчанию. Однако автор все равно должен предоставить код, чтобы заменить этот пустой аргумент его значением по умолчанию.

мои утилиты, которые являются более сложными, со многими параметрами, все из которых имеют аргументы по умолчанию, часто будут использовать интерфейс пары свойств/значений для аргументов по умолчанию. Эта базовая парадигма рассматривается в графических инструментах handle в matlab, а также в optimset, odeset и т. д.

в качестве средства для работы с этими пары свойство/значение, вам необходимо узнайте о varargin, как способ ввода полностью переменного числа аргументов в функцию. Я написал (и опубликовал) утилиту для работы с такими парами свойств / значений,parse_pv_pairs.м. Это поможет вам конвертировать пары свойств / значений в структуру matlab. Он также позволяет задать значения по умолчанию для каждого параметра. Преобразование громоздкого списка параметров в структуру-очень хороший способ передать их в MATLAB.

Это мой простой способ, чтобы установить значения по умолчанию для функции "попробуй":

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

С уважением!

Я обнаружил, что parseArgs функция может быть очень полезной.

существует также "хак", который может быть использован, хотя он может быть удален из matlab в какой-то момент: Функция eval принимает два аргумента из которых второй выполняется, если произошла ошибка с первым.

таким образом, мы можем использовать

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

использовать значение 1 по умолчанию для аргумента

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

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

просто подумал, что я поделюсь им.

Я в замешательстве никто не указал этот блог Лорен, один из разработчиков Matlab. Подход основан на varargin и избегает всех этих бесконечных и болезненных if-then-else или switch случаи с запутанными условиями. Когда есть немного значения по умолчанию, эффект эмо. Вот пример из связанного блог:

function y = somefun2Alt(a,b,varargin)
% Some function that requires 2 inputs and has some optional inputs.

% only want 3 optional inputs at most
numvarargs = length(varargin);
if numvarargs > 3
    error('myfuns:somefun2Alt:TooManyInputs', ...
        'requires at most 3 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{:};

если вы все еще не понимаете, тогда попробуйте прочитать весь пост в блоге Лорен. У меня есть написал продолжение блоге что касается отсутствует позиционные значения по умолчанию. Я имею в виду, что вы могли бы написать что-то вроде:

somefun2Alt(a, b, '', 42)

и по-прежнему имеют значение по умолчанию eps значение

после того, как стало известно ASSIGNIN (спасибо ответ by b3) и ЕВАЛИН я написал две функции, чтобы, наконец, получить очень простую структуру вызова:

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 ')']) ;

Это более или менее снято с руководство 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

Matlab не предоставляет механизм для этого, но вы можете построить его в коде userland, который более терсер, чем 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 help - подсказки автозаполнения в командной строке-не работает для функций varargin. Но это довольно удобно.

вы можете использовать в 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;
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.

если бы вы использовали октаву, вы могли бы сделать это так - но, к сожалению, matlab не поддерживает эту возможность

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

(взято из doc)

мне нравится делать это несколько более объектно-ориентированным способом. Перед вызовом wave () сохраните некоторые из ваших аргументов в структуре, например. один из них называется параметры:

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

в волновой функции затем проверьте, содержит ли параметры структуры поле с именем '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

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