Как установить значения по умолчанию для параметров функций в Matlab? - PullRequest
117 голосов
/ 28 апреля 2009

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

145 голосов
/ 28 апреля 2009

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

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

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

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

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

57 голосов
/ 02 мая 2009

Я использовал объект 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, поэтому оставил валидатор пустым.

18 голосов
/ 20 февраля 2011

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

function output = fun(input)
   if ~exist('input','var'), input='BlahBlahBlah'; end
   ...
end
10 голосов
/ 28 апреля 2009

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

5 голосов
/ 09 июня 2015

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

Привет! * * 1004

3 голосов
/ 06 апреля 2014

Я в замешательстве, никто не указал на этот пост Лорена, одного из разработчиков 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 для параметра tol (и, конечно, @magic обратный вызов для func). Код Лорен позволяет это с небольшой, но хитрой модификацией.

Наконец, лишь несколько преимуществ этого подхода:

  1. Даже при большом количестве значений по умолчанию стандартный код не становится огромным (в отличие от семейства if-then-else подходов, которые становятся длиннее с каждым новым значением по умолчанию)
  2. Все значения по умолчанию находятся в одном месте. Если что-то из этого нужно изменить, у вас есть только одно место, чтобы посмотреть.

Говорят, что есть и недостаток. Когда вы наберете функцию в оболочке Matlab и забудете ее параметры, вы увидите бесполезную подсказку varargin. Чтобы справиться с этим, советуем написать содержательное предложение об использовании.

3 голосов
/ 16 января 2014

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

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

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

3 голосов
/ 07 декабря 2010

Существует также «взлом», который можно использовать, хотя он может быть удален из Matlab в некоторый момент: Функция eval фактически принимает два аргумента, второй из которых запускается, если с первым произошла ошибка.

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

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

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

3 голосов
/ 29 апреля 2009

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

2 голосов
/ 22 марта 2011

Узнав о ASSIGNIN (благодаря этот ответ от b3 ) и 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 ')']) ;
...