Wie lege ich Standardwerte für Funktionsparameter in Matlab fest?

126

Ist es möglich, Standardargumente in Matlab zu haben? Zum Beispiel hier:

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

Ich möchte, dass die wahre Lösung ein optionales Argument für die Wellenfunktion ist. Wenn es möglich ist, kann jemand den richtigen Weg zeigen, dies zu tun? Derzeit versuche ich, was ich oben gepostet habe und bekomme:

??? 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.
Scott
quelle

Antworten:

151

Es gibt keinen direkten Weg, dies so zu tun, wie Sie es versucht haben.

Der übliche Ansatz besteht darin, "varargs" zu verwenden und die Anzahl der Argumente zu überprüfen. Etwas wie:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Es gibt ein paar ausgefallenere Dinge, mit denen Sie isemptyusw. arbeiten können, und Sie sollten sich Matlab Central für einige Pakete ansehen, die diese Art von Dingen bündeln.

Vielleicht haben Sie einen Blick auf haben varargin, nargchkusw. Sie sind nützliche Funktionen für diese Art der Sache. Mit varargs können Sie eine variable Anzahl von Endargumenten belassen, aber dies führt Sie nicht um das Problem der Standardwerte für einige / alle von ihnen herum.

Simon
quelle
58

Ich habe das inputParserObjekt verwendet, um Standardoptionen festzulegen. Matlab akzeptiert das in der Frage angegebene Python-ähnliche Format nicht, aber Sie sollten die Funktion folgendermaßen aufrufen können:

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

Nachdem Sie die waveFunktion wie folgt definiert haben:

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{:});

Jetzt sind die an die Funktion übergebenen Werte über verfügbar i_p.Results. Außerdem war ich mir nicht sicher, wie ich überprüfen sollte, ob der übergebene Parameter ftruetatsächlich eine inlineFunktion ist, also ließ ich den Validator leer.

Matt
quelle
7
Wie ich am besten beurteilen kann, ist dies die bevorzugte Methode. Es ist sauber, selbstdokumentierend (mehr noch eine Reihe von if narginStatemens), wartungsfreundlich, kompakt und flexibel.
JnBrymn
19

Ein anderer etwas weniger hackiger Weg ist

function output = fun(input)
   if ~exist('input','var'), input='BlahBlahBlah'; end
   ...
end
Peter
quelle
Diese Option funktioniert nicht, wenn Sie MATLAB Coder zum Generieren von C-Code verwenden möchten, da Coder die Funktion "exist" nicht unterstützt.
Dave Tillman
10

Ja, es könnte wirklich schön sein, die Fähigkeit zu haben, das zu tun, was Sie geschrieben haben. In MATLAB ist dies jedoch nicht möglich. Viele meiner Dienstprogramme, die Standardeinstellungen für die Argumente zulassen, werden am Anfang mit expliziten Überprüfungen wie folgt geschrieben:

if (nargin<3) or isempty(myParameterName)
  MyParameterName = defaultValue;
elseif (.... tests for non-validity of the value actually provided ...)
  error('The sky is falling!')
end

Ok, also würde ich im Allgemeinen eine bessere, aussagekräftigere Fehlermeldung anwenden. Beachten Sie, dass die Prüfung auf eine leere Variable es dem Benutzer ermöglicht, ein leeres Klammerpaar [] als Platzhalter für eine Variable zu übergeben, die ihren Standardwert annimmt. Der Autor muss dennoch den Code angeben, um dieses leere Argument durch seinen Standardwert zu ersetzen.

Meine komplexeren Dienstprogramme mit VIELEN Parametern, die alle Standardargumente haben, verwenden häufig eine Eigenschaft / Wert-Paar-Schnittstelle für Standardargumente. Dieses grundlegende Paradigma wird in den Handle-Grafik-Tools in Matlab sowie in Optimset, Odeset usw. gesehen.

Um mit diesen Eigenschafts- / Wertepaaren arbeiten zu können, müssen Sie sich mit Varargin vertraut machen, um eine vollständig variable Anzahl von Argumenten in eine Funktion eingeben zu können. Ich habe ein Dienstprogramm geschrieben (und veröffentlicht), um mit solchen Eigenschaft / Wert-Paaren zu arbeiten, parse_pv_pairs.m . Es hilft Ihnen, Eigenschafts- / Wertepaare in eine Matlab-Struktur zu konvertieren. Außerdem können Sie für jeden Parameter Standardwerte angeben. Das Konvertieren einer unhandlichen Liste von Parametern in eine Struktur ist eine SEHR gute Möglichkeit, sie in MATLAB weiterzugeben.


quelle
6

Dies ist meine einfache Möglichkeit, mit "try" Standardwerte für eine Funktion festzulegen:

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

Grüße!

Jonay Cruz
quelle
3

Ich habe festgestellt, dass die Funktion parseArgs sehr hilfreich sein kann.

Herr Fooz
quelle
3

Es gibt auch einen 'Hack', der verwendet werden kann, obwohl er möglicherweise irgendwann aus dem Matlab entfernt wird: Die Funktion eval akzeptiert tatsächlich zwei Argumente, von denen das zweite ausgeführt wird, wenn beim ersten ein Fehler aufgetreten ist.

So können wir verwenden

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

um den Wert 1 als Standard für das Argument zu verwenden

vuakko
quelle
3

Ich glaube, ich habe einen ziemlich raffinierten Weg gefunden, um mit diesem Problem umzugehen, indem ich nur drei Codezeilen (mit Ausnahme von Zeilenumbrüchen) verwendet habe. Das Folgende wird direkt von einer Funktion entfernt, die ich schreibe, und es scheint wie gewünscht zu funktionieren:

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

Ich dachte nur, ich würde es teilen.

Bonnevie
quelle
3

Ich bin verwirrt, dass niemand auf diesen Blog-Beitrag von Loren, einem der Entwickler von Matlab, hingewiesen hat . Der Ansatz basiert auf vararginund vermeidet all diese endlosen und schmerzhaft if-then-elseoder switchFälle mit gewundenen Bedingungen. Wenn es einige Standardwerte gibt, ist der Effekt dramatisch . Hier ist ein Beispiel aus dem verlinkten Blog:

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{:};

Wenn Sie es immer noch nicht verstehen, lesen Sie den gesamten Blog-Beitrag von Loren. Ich habe einen nachfolgenden Blog-Beitrag geschrieben, der sich mit fehlenden Positionsstandardwerten befasst . Ich meine, du könntest so etwas schreiben wie:

somefun2Alt(a, b, '', 42)

und haben immer noch den Standardwert epsfür den tolParameter (und @magicRückruf für funcnatürlich). Lorens Code erlaubt dies mit einer kleinen, aber kniffligen Modifikation.

Zum Schluss noch einige Vorteile dieses Ansatzes:

  1. Selbst bei vielen if-then-elseStandardeinstellungen wird der Boilerplate-Code nicht riesig (im Gegensatz zur Familie der Ansätze, die mit jedem neuen Standardwert länger werden).
  2. Alle Standardeinstellungen befinden sich an einem Ort. Wenn sich eines davon ändern muss, haben Sie nur einen Ort zum Anschauen.

Trooth gesagt, es gibt auch einen Nachteil. Wenn Sie die Funktion in die Matlab-Shell eingeben und ihre Parameter vergessen, wird eine nicht hilfreiche Funktion vararginals Hinweis angezeigt. Um damit umzugehen, wird empfohlen, eine aussagekräftige Verwendungsklausel zu schreiben.

alisianoi
quelle
Der Link zu Ihrem nachfolgenden Blog-Beitrag ist fehlerhaft. kannst du das Reparieren?
Equaeghe
2

Nachdem ich ASSIGNIN (dank dieser Antwort von b3 ) und EVALIN kennengelernt hatte, schrieb ich zwei Funktionen, um endlich eine sehr einfache Aufrufstruktur zu erhalten:

setParameterDefault('fTrue', inline('0'));

Hier ist die Auflistung:

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

und

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 ')']) ;
Tobias Kienzler
quelle
1

Dies ist mehr oder weniger aus dem Matlab-Handbuch entnommen . Ich habe nur vorübergehende Erfahrung ...

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
Kyle
quelle
1
Es gab ein paar Fehler im Code, die ich korrigiert habe. Zunächst muss "optargin" definiert werden. Zweitens ist "varargin" ein Zellenarray, das alle nachfolgenden Eingaben sammelt. Sie müssen also die Zellarray-Indizierung verwenden, um Werte daraus zu entfernen.
Gnovice
Ich muss meine Sicht überprüfen lassen; Ich schwöre, ich habe gestern nichts davon im Handbuch gesehen :(
Kyle
@kyle: Keine Sorge, wir machen alle Fehler. Deshalb mag ich den Wiki-Stil von SO: Wenn ich einen dummen Tippfehler mache, ist normalerweise jemand anderes in der Nähe, der ihn fangen und schnell für mich reparieren kann. =)
Gnovice
1

Matlab bietet hierfür keinen Mechanismus, aber Sie können einen im Userland-Code erstellen, der kürzer als die Sequenzen inputParser oder "if nargin <1 ..." ist.

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

Dann können Sie es in Ihren Funktionen wie folgt aufrufen:

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

Die Formatierung ist eine Konvention, mit der Sie von Parameternamen auf ihre Standardwerte herunterlesen können. Sie können Ihre getargs () mit optionalen Parametertypspezifikationen (zur Fehlererkennung oder impliziten Konvertierung) und Argumentanzahlbereichen erweitern.

Dieser Ansatz weist zwei Nachteile auf. Erstens ist es langsam, sodass Sie es nicht für Funktionen verwenden möchten, die in Schleifen aufgerufen werden. Zweitens funktioniert die Funktionshilfe von Matlab - die Hinweise zur automatischen Vervollständigung in der Befehlszeile - nicht für Varargin-Funktionen. Aber es ist ziemlich praktisch.

Andrew Janke
quelle
0

Möglicherweise möchten Sie den parseparamsBefehl in matlab verwenden. Die Verwendung würde folgendermaßen aussehen:

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
quelle
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;

zB f(2,4,'c',3)bewirkt, dass der Parameter c3 ist.

Tobias Kienzler
quelle
0

Wenn Sie Oktave verwenden würden, könnten Sie dies so tun - aber leider unterstützt Matlab diese Möglichkeit nicht

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

(aus dem Dokument entnommen )

wuschLOR
quelle
0

Ich mache das gerne etwas objektorientierter. Speichern Sie vor dem Aufruf von wave () einige Ihrer Argumente in einer Struktur, z. man nannte Parameter:

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

Überprüfen Sie dann innerhalb der Wave-Funktion, ob die Strukturparameter ein Feld namens 'flag' enthalten und wenn ja, ob sein Wert nicht leer ist. Weisen Sie ihm dann einen Standardwert zu, den Sie zuvor definiert haben, oder den Wert, der als Argument in der Parameterstruktur angegeben ist:

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

Dies erleichtert die Verarbeitung einer großen Anzahl von Argumenten, da dies nicht von der Reihenfolge der angegebenen Argumente abhängt. Es ist jedoch auch hilfreich, wenn Sie später weitere Argumente hinzufügen müssen, da Sie dazu die Funktionssignatur nicht ändern müssen.

CheshireCat
quelle
Warum nicht dem MATLAB-Standard für Name-Wert-Paare folgen? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo
Dies ist sicherlich auch eine Option, insbesondere wenn man nur wenige Parameter hat. Das Aufrufen von wave () mit einer großen Anzahl von Name-Wert-Paaren kann jedoch die Lesbarkeit des Codes beeinträchtigen. Ich bevorzuge es daher oft, bestimmte Eingaben in Strukturen zu gruppieren.
CheshireCat