Comment définir les valeurs par défaut des paramètres de fonctions dans Matlab?


127

Est-il possible d'avoir des arguments par défaut dans Matlab? Par exemple, ici:

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

J'aimerais que la vraie solution soit un argument optionnel de la fonction wave. Si c'est possible, quelqu'un peut-il démontrer la bonne façon de procéder? Actuellement, j'essaye ce que j'ai posté ci-dessus et j'obtiens:

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

Réponses:


151

Il n'y a pas de moyen direct de faire cela comme vous l'avez essayé.

L'approche habituelle consiste à utiliser "varargs" et à comparer le nombre d'arguments. Quelque chose comme:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Il y a quelques choses plus sophistiquées que vous pouvez faire isempty, etc.

Vous pouvez jeter un oeil à varargin, nargchketc. Ils sont des fonctions utiles pour ce genre de chose. varargs vous permet de laisser un nombre variable d'arguments finaux, mais cela ne vous permet pas de contourner le problème des valeurs par défaut pour certains / tous.


58

J'ai utilisé l' inputParserobjet pour régler les options par défaut. Matlab n'acceptera pas le format de type python que vous avez spécifié dans la question, mais vous devriez pouvoir appeler la fonction comme ceci:

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

Après avoir défini la wavefonction comme ceci:

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

Les valeurs transmises à la fonction sont désormais disponibles via i_p.Results. De plus, je ne savais pas comment valider que le paramètre passé pour ftrueétait en fait une inlinefonction, alors laissez le validateur vide.


7
Mieux que je peux dire, ce , est la méthode préférée. Il est propre, auto-documenté (plus encore un tas d' if narginétats), facile à entretenir, compact et flexible.
JnBrymn

19

Une autre manière légèrement moins hacky est

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

Cette option ne fonctionne pas si vous allez utiliser MATLAB Coder pour générer du code C, car Coder ne prend pas en charge la fonction "exister".
Dave Tillman

10

Oui, ce serait peut-être vraiment bien d'avoir la capacité de faire ce que vous avez écrit. Mais ce n'est pas possible dans MATLAB. Beaucoup de mes utilitaires qui autorisent les valeurs par défaut pour les arguments ont tendance à être écrits avec des vérifications explicites au début comme ceci:

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, donc j'appliquerais généralement un meilleur message d'erreur plus descriptif. Voir que la vérification d'une variable vide permet à l'utilisateur de passer une paire de crochets vides, [], comme espace réservé pour une variable qui prendra sa valeur par défaut. L'auteur doit toujours fournir le code pour remplacer cet argument vide par sa valeur par défaut.

Mes utilitaires qui sont plus sophistiqués, avec BEAUCOUP de paramètres, qui ont tous des arguments par défaut, utiliseront souvent une interface de paire propriété / valeur pour les arguments par défaut. Ce paradigme de base est vu dans les outils graphiques de poignée de matlab, ainsi que dans optimset, odeset, etc.

Pour travailler avec ces paires propriété / valeur, vous devrez vous renseigner sur varargin, comme moyen de saisir un nombre entièrement variable d'arguments dans une fonction. J'ai écrit (et posté) un utilitaire pour travailler avec de telles paires propriété / valeur, parse_pv_pairs.m . Il vous aide à convertir des paires propriété / valeur en une structure matlab. Il vous permet également de fournir des valeurs par défaut pour chaque paramètre. Convertir une liste de paramètres peu maniable en une structure est une TRÈS belle façon de les transmettre dans MATLAB.


7

C'est ma façon simple de définir les valeurs par défaut d'une fonction, en utilisant "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

Cordialement!



3

Il existe également un «hack» qui peut être utilisé bien qu'il puisse être supprimé de matlab à un moment donné: La fonction eval accepte en fait deux arguments dont le second est exécuté si une erreur s'est produite avec le premier.

Ainsi, nous pouvons utiliser

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

pour utiliser la valeur 1 par défaut pour l'argument


3

Je crois que j'ai trouvé un moyen assez astucieux de résoudre ce problème, en ne prenant que trois lignes de code (sauf les retours à la ligne). Ce qui suit est tiré directement d'une fonction que j'écris, et cela semble fonctionner comme vous le souhaitez:

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

Je pensais juste que je le partagerais.


3

Je suis confus que personne n'a signalé ce billet de blog par Loren, l'un des développeurs de Matlab. L'approche est basée sur vararginet évite tous ces interminables et douloureux if-then-elseou switchcas avec des conditions alambiquées. Lorsqu'il existe quelques valeurs par défaut, l'effet est dramatique . Voici un exemple du blog lié:

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

Si vous ne comprenez toujours pas, essayez de lire l'intégralité du billet de blog de Loren. J'ai écrit un article de blog de suivi qui traite des valeurs par défaut de position manquantes . Je veux dire que vous pourriez écrire quelque chose comme:

somefun2Alt(a, b, '', 42)

et ont toujours la epsvaleur par défaut pour le tolparamètre (et le @magicrappel pour funcbien sûr). Le code de Loren permet cela avec une modification légère mais délicate.

Enfin, quelques avantages de cette approche:

  1. Même avec beaucoup de valeurs par défaut, le code standard ne devient pas énorme (contrairement à la famille d' if-then-elseapproches, qui s'allonge à chaque nouvelle valeur par défaut)
  2. Tous les paramètres par défaut se trouvent au même endroit. Si l'un de ces éléments doit changer, vous n'avez qu'un seul endroit à regarder.

À vrai dire, il y a aussi un inconvénient. Lorsque vous tapez la fonction dans le shell Matlab et oubliez ses paramètres, vous verrez vararginun indicateur inutile . Pour gérer cela, il est conseillé d'écrire une clause d'utilisation significative.


Le lien vers votre article de blog de suivi est rompu; Peux-tu le réparer?
equaeghe

2

Après avoir pris connaissance de ASSIGNIN (grâce à cette réponse de b3 ) et EVALIN j'ai écrit deux fonctions pour enfin obtenir une structure d'appel très simple:

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

Voici la liste:

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

et

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

Ceci est plus ou moins tiré du manuel Matlab ; Je n'ai qu'une expérience passagère ...

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
Il y avait quelques erreurs dans le code que j'ai corrigées. Tout d'abord, "optargin" doit être défini. Deuxièmement, "varargin" est un tableau de cellules qui collecte toutes les entrées suivantes, vous devez donc utiliser l'indexation de tableau de cellules pour en supprimer des valeurs.
gnovice

J'ai besoin de faire vérifier ma vision; Je jure que je n'ai rien vu de tout cela dans le manuel hier :(
kyle

@kyle: Ne vous inquiétez pas, nous faisons tous des erreurs. C'est pourquoi j'aime le style wiki-ish de SO: si je fais une faute de frappe stupide, il y a généralement quelqu'un d'autre autour qui peut l'attraper et le réparer rapidement pour moi. =)
gnovice

1

Matlab ne fournit pas de mécanisme pour cela, mais vous pouvez en construire un dans le code utilisateur qui soit plus laconique que les séquences inputParser ou "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

Ensuite, vous pouvez l'appeler dans vos fonctions comme ceci:

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

La mise en forme est une convention qui vous permet de lire les noms de paramètres jusqu'à leurs valeurs par défaut. Vous pouvez étendre votre getargs () avec des spécifications de type de paramètre facultatives (pour la détection d'erreur ou la conversion implicite) et des plages de nombre d'arguments.

Cette approche présente deux inconvénients. Tout d'abord, il est lent, vous ne voulez donc pas l'utiliser pour des fonctions appelées en boucle. Deuxièmement, l'aide aux fonctions de Matlab - les conseils de saisie semi-automatique sur la ligne de commande - ne fonctionne pas pour les fonctions varargin. Mais c'est assez pratique.


0

vous voudrez peut-être utiliser la parseparamscommande dans matlab; l'utilisation ressemblerait à:

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;

Par exemple f(2,4,'c',3), le paramètre cest 3.


0

si vous utilisiez octave, vous pourriez le faire comme ceci - mais malheureusement, matlab ne prend pas en charge cette possibilité

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

(extrait du doc )


0

J'aime faire cela d'une manière un peu plus orientée objet. Avant d'appeler wave (), sauvegardez certains de vos arguments dans une structure, par exemple. un appelé paramètres:

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

Dans la fonction wave, vérifiez si les paramètres de la structure contiennent un champ appelé 'flag' et si c'est le cas, si sa valeur n'est pas vide. Attribuez-lui ensuite une valeur par défaut que vous avez définie auparavant, ou la valeur donnée en argument dans la structure des paramètres:

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

Cela facilite la gestion d'un grand nombre d'arguments, car cela ne dépend pas de l'ordre des arguments donnés. Cela dit, il est également utile si vous devez ajouter plus d'arguments ultérieurement, car vous n'avez pas à modifier la signature des fonctions pour le faire.


Pourquoi ne pas suivre la norme MATLAB des paires nom-valeur? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo

C'est certainement une option aussi, surtout quand on n'a que peu de paramètres. L'appel de wave () avec un grand nombre de paires nom-valeur peut cependant réduire la lisibilité du code. Je préfère donc souvent regrouper certaines entrées en structs.
CheshireCat
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.