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

127

Можно ли иметь аргументы по умолчанию в 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.
Скотт
источник

Ответы:

151

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

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

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

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

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

Саймон
источник
58

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

Matt
источник
7
Насколько я могу судить, это предпочтительный метод. Он чистый, самодокументируемый (тем более, куча if narginстатей), простой в обслуживании, компактный и гибкий.
JnBrymn 07
19

Другой менее хакерский способ -

function output = fun(input)
   if ~exist('input','var'), input='BlahBlahBlah'; end
   ...
end
Питер
источник
Эта опция не работает, если вы собираетесь использовать MATLAB Coder для генерации кода C, поскольку Coder не поддерживает функцию «exist».
Дэйв Тиллман,
10

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


источник
7

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

С уважением!

Джонай Круз
источник
3

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

Г-н Фуз
источник
3

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

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

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

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

vuakko
источник
3

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

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

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

Bonnevie
источник
3

Я смущен, что никто не указал на это сообщение в блоге Лорен, одного из разработчиков 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подсказку. Чтобы справиться с этим, вам рекомендуется написать осмысленное использование.

alisianoi
источник
Ссылка на ваш последующий пост в блоге не работает; ты можешь починить это?
equaeghe
2

После того, как стало известно о 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 ')']) ;
Тобиас Кинцлер
источник
1

Это более или менее взято из руководства 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
рукав моря
источник
1
В коде было несколько ошибок, которые я исправил. Во-первых, необходимо определить «оптаргин». Во-вторых, «varargin» - это массив ячеек, который собирает все последующие входные данные, поэтому вы должны использовать индексацию массива ячеек, чтобы удалить из него значения.
gnovice
Мне нужно проверить зрение; Клянусь, я вчера не видел ничего из этого в руководстве :(
kyle
@kyle: Не волнуйтесь, мы все делаем ошибки. Вот почему мне нравится вики-стиль SO: если я допускаю какую-нибудь глупую опечатку, обычно есть кто-то, кто может ее уловить и быстро исправить для меня. =)
gnovice
1

Matlab не предоставляет механизма для этого, но вы можете создать такой код в пользовательском коде, который короче, чем inputParser или последовательности «if nargin <1 ...».

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 - подсказки автозаполнения в командной строке - не работают для функций varargin. Но это довольно удобно.

Эндрю Янке
источник
0

вы можете использовать эту parseparamsкоманду в 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;
shabbychef
источник
0
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.

Тобиас Кинцлер
источник
0

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

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

(взято из документа )

wuschLOR
источник
0

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

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

Чеширский кот
источник
Почему бы не следовать стандарту MATLAB пар имя-значение? wave(a,b,'flag',42,'fTrue',1)
Крис Луенго
Это, безусловно, тоже вариант, особенно когда у вас мало параметров. Однако вызов wave () с большим количеством пар имя-значение может снизить читабельность кода. Поэтому я часто предпочитаю сгруппировать определенные входные данные в структуры.
CheshireCat