web-dev-qa-db-fra.com

Comment définir les valeurs par défaut pour les paramètres de fonction dans Matlab?

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

Je voudrais que la vraie solution soit un argument optionnel à la fonction wave. Si cela est possible, quelqu'un peut-il démontrer la bonne façon de procéder? Actuellement, j'essaie ce que j'ai posté ci-dessus et je reçois:

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

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

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

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Il existe quelques options plus sophistiquées que vous pouvez utiliser avec isempty, etc., et vous voudrez peut-être consulter Matlab central pour certains packages regroupant ce type d'éléments.

Vous pouvez consulter varargin, nargchk, etc. Ce sont des fonctions utiles pour ce genre de choses. varargs vous permet de laisser un nombre variable d'arguments finaux, mais cela ne vous évite pas le problème des valeurs par défaut pour certains/tous.

142
simon

J'ai utilisé l'objet inputParser pour traiter la définition des options par défaut. Matlab n'acceptera pas le format 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 fonction wave 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{:});

Maintenant, les valeurs transmises à la fonction sont disponibles via i_p.Results. De plus, je ne savais pas comment valider que le paramètre transmis pour ftrue était en réalité une fonction inline et laissait donc le validateur vierge.

56
Matt

Une autre façon un peu moins hacky est

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

Oui, il serait peut-être très agréable 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 arguments 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

D'accord, j'appliquerais donc généralement un meilleur message d'erreur plus descriptif. Veillez à ce que la recherche d'une variable vide permette à l'utilisateur de passer une paire de crochets vides, [], en tant qu'espace réservé pour une variable qui prendra sa valeur par défaut. L'auteur doit néanmoins fournir le code pour remplacer cet argument vide par sa valeur par défaut.

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

Pour pouvoir utiliser ces paires propriété/valeur, vous devez en savoir plus sur varargin, en tant que 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 les 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 compliquée de paramètres en une structure est un moyen TRÈS agréable de les transmettre dans MATLAB.

10
user85109

Voici un moyen 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!

5
Jonay Cruz

J'ai trouvé que la fonction parseArgs peut être très utile.

3
Mr Fooz

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

Ainsi, nous pouvons utiliser

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

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

3
vuakko

Je suis confus que personne n'a signalé ce billet de blog de Loren, l'un des développeurs de Matlab. L’approche est basée sur varargin et évite tous ces cas sans fin et douloureux if-then-else ou switch avec des conditions compliquées. Quand il y a quelques valeurs par défaut, l'effet est dramatique. Voici un exemple tiré 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 l'obtenez toujours pas, essayez de lire l'intégralité du billet de blog de Loren. J'ai écrit un article de suivi blog qui traite des valeurs par défaut manquantes. Je veux dire que vous pourriez écrire quelque chose comme:

somefun2Alt(a, b, '', 42)

et toujours avoir la valeur par défaut eps pour le paramètre tol (et @magic callback pour func bien sûr). Le code de Loren le permet 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 n'est pas énorme (contrairement à la famille d'approches if-then-else qui s'allongent à chaque nouvelle valeur par défaut)
  2. Tous les défauts sont au même endroit. Si l'un de ceux-ci doit changer, vous n'avez qu'un seul endroit à regarder.

Trooth soit dit, il y a aussi un inconvénient. Lorsque vous tapez la fonction dans Matlab Shell et oubliez ses paramètres, vous verrez une varargin inutile comme un indice. Pour résoudre ce problème, il vous est conseillé de rédiger une clause d'utilisation significative.

3
alisianoi

Je pense avoir trouvé un moyen assez astucieux de traiter ce problème, en n'utilisant que trois lignes de code (sauf les retours à la ligne). Ce qui suit est tiré directement d’une fonction que j’écris et semble fonctionner comme souhaité:

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
Bonnevie

Après avoir pris connaissance de ASSIGNIN (grâce à cette réponse de b3 ) et de EVALIN j'ai écrit deux fonctions pour obtenir finalement 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 ')']) ;
2
Tobias Kienzler

Ceci est plus ou moins soulevé dans le 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
kyle

Matlab ne fournit pas de mécanisme pour cela, mais vous pouvez en créer un dans le code utilisateur qui est plus ordonné 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ètre 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 une conversion implicite) et des plages de comptage d'arguments.

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

1
Andrew Janke

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

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
shabbychef
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) fait que le paramètre c soit égal à 3.

0
Tobias Kienzler

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

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

(tiré du doc )

0
wuschLOR

J'aime faire cela d'une manière un peu plus orientée objet . Avant d'appeler wave (), enregistrez 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 le paramètre struct contient un champ appelé "flag" et, si c'est le cas, si sa valeur n'est pas vide. Puis attribuez-lui une valeur par défaut que vous avez définie auparavant ou la valeur donnée en tant qu'argument dans la structure de 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 d’ajouter ultérieurement des arguments, car il n’est pas nécessaire de modifier la signature des fonctions pour le faire.

0
CheshireCat