Як встановити значення за замовчуванням для параметрів функцій у 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функцією, тому валідатор залишив порожнім.


7
Як я найкраще можу сказати, це найкращий метод. Це чисте, самодокументоване (тим більше, купа if narginштампів), просте в обслуговуванні, компактне та гнучко.
JnBrymn

19

Ще один трохи менш хакітний спосіб

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

Ця опція не працює, якщо ви збираєтеся використовувати кодер MATLAB для генерації коду С, оскільки Coder не підтримує функцію "існує".
Дейв Тілман

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, а також в оптиміті, одасеті тощо.

Як засіб для роботи з цими парами властивості / значення, вам потрібно буде дізнатися про varargin, як спосіб введення цілком змінної кількості аргументів до функції. Я написав (і розмістив) утиліту для роботи з такими парами властивостей / значень, parse_pv_pairs.m . Це допомагає вам перетворити пари властивостей / значень у структуру matlab. Це також дозволяє надати значення за замовчуванням для кожного параметра. Перетворення нелегкого списку параметрів у структуру - ДУЖЕ приємний спосіб передавати їх навколо в MATLAB.


7

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

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

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

Таким чином ми можемо використовувати

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

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


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

Просто думав, що поділюсь цим.


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як підказку. Щоб вирішити це, вам рекомендується написати змістовне положення про використання.


Посилання на вашу подальшу публікацію в блозі порушено; ви можете це виправити?
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 не забезпечує механізм цього, але ви можете побудувати його в коді userland, який довше 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'});

Форматування - це умова, яка дозволяє читати з імен параметрів до їх значень за замовчуванням. Ви можете розширити свої гетарги () за допомогою додаткових специфікацій типу параметрів (для виявлення помилок або неявного перетворення) та діапазонів підрахунку аргументів.

У цього підходу є два недоліки. По-перше, це повільно, тому ви не хочете використовувати його для функцій, які викликаються циклами. По-друге, допомога щодо функції 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;

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

(взято з док. )


0

Мені подобається це робити дещо більш об'єктно орієнтованим способом. Перед тим, як викликати хвилю (), збережіть деякі свої аргументи в структурі, наприклад. один звані параметри:

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

Потім у хвильовій функції перевірте, чи містить параметри структури поле під назвою "прапор", і якщо так, чи значення його не порожнє. Потім призначте йому вісімнадцяте значення за замовчуванням, яке ви визначили раніше, або значення, задане як аргумент у структурі параметрів:

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)
Кріс Луенго

Це, безумовно, також варіант, особливо коли в них лише кілька параметрів. Однак виклик хвилі () з великою кількістю пар імен-значень може зменшити читабельність коду. Тому я часто вважаю за краще групувати певні вкладення в структури.
CheshireCat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.