Come posso impostare i valori predefiniti per i parametri delle funzioni in Matlab?


127

È possibile avere argomenti predefiniti in Matlab? Ad esempio, qui:

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

Vorrei che la vera soluzione fosse un argomento facoltativo per la funzione d'onda. Se è possibile, qualcuno può dimostrare il modo corretto per farlo? Attualmente, sto provando quello che ho pubblicato sopra e ottengo:

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

Risposte:


151

Non esiste un modo diretto per farlo come hai tentato.

L'approccio abituale è usare "varargs" e verificare il numero di argomenti. Qualcosa di simile a:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Ci sono alcune cose più fantasiose che puoi fare isempty, ecc. E potresti voler guardare Matlab central per alcuni pacchetti che raggruppano questo tipo di cose.

Si potrebbe avere uno sguardo varargin, nargchkecc Sono funzioni utili per questo genere di cose. varargs ti consente di lasciare un numero variabile di argomenti finali, ma questo non ti risolve il problema dei valori predefiniti per alcuni / tutti.


58

Ho usato l' inputParseroggetto per gestire le impostazioni predefinite. Matlab non accetterà il formato simile a Python specificato nella domanda, ma dovresti essere in grado di chiamare la funzione in questo modo:

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

Dopo aver definito la wavefunzione in questo modo:

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

Ora i valori passati nella funzione sono disponibili tramite i_p.Results. Inoltre, non ero sicuro di come convalidare che il parametro passato per ftrueera in realtà una inlinefunzione, quindi il validatore è rimasto vuoto.


7
Come meglio posso dire, questo è il metodo preferito. È pulito, auto-documentato (più un gruppo di stati if nargin), facile da mantenere, compatto e flessibile.
JnBrymn,

19

Un altro modo leggermente meno confuso è

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

Questa opzione non funziona se si intende utilizzare MATLAB Coder per generare il codice C, poiché Coder non supporta la funzione "esiste".
Dave Tillman,

10

Sì, potrebbe essere davvero bello avere la capacità di fare come hai scritto. Ma non è possibile in MATLAB. Molte delle mie utilità che consentono valori predefiniti per gli argomenti tendono ad essere scritte con controlli espliciti all'inizio in questo modo:

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, quindi generalmente applicherei un messaggio di errore migliore e più descrittivo. Verifica che il controllo per una variabile vuota consenta all'utente di passare una coppia vuota di parentesi, [], come segnaposto per una variabile che assumerà il suo valore predefinito. L'autore deve comunque fornire il codice per sostituire quell'argomento vuoto con il suo valore predefinito.

Le mie utilità più sofisticate, con MOLTI parametri, tutti con argomenti predefiniti, useranno spesso un'interfaccia coppia proprietà / valore per argomenti predefiniti. Questo paradigma di base è visibile negli strumenti grafici handle in matlab, così come in optimset, odeset, ecc.

Come mezzo per lavorare con queste coppie proprietà / valore, dovrai conoscere varargin, come un modo per inserire un numero completamente variabile di argomenti in una funzione. Ho scritto (e pubblicato) un'utility per lavorare con tali coppie proprietà / valore, parse_pv_pairs.m . Ti aiuta a convertire le coppie proprietà / valore in una struttura matlab. Inoltre, consente di fornire valori predefiniti per ciascun parametro. Convertire un ingombrante elenco di parametri in una struttura è un modo MOLTO piacevole per passarli in giro in MATLAB.


7

Questo è il mio modo semplice di impostare i valori predefiniti su una funzione, usando "provare":

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

Saluti!



3

C'è anche un 'hack' che può essere usato sebbene possa essere rimosso da Matlab ad un certo punto: La funzione eval accetta effettivamente due argomenti di cui il secondo viene eseguito se si è verificato un errore con il primo.

Quindi possiamo usare

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

per utilizzare il valore 1 come predefinito per l'argomento


3

Credo di aver trovato un modo abbastanza ingegnoso per affrontare questo problema, occupando solo tre righe di codice (salvo il ritorno a capo). Quanto segue viene sollevato direttamente da una funzione che sto scrivendo e sembra funzionare come desiderato:

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

Ho pensato di condividerlo.


3

Sono confuso che nessuno abbia sottolineato questo post del blog di Loren, uno degli sviluppatori di Matlab. L'approccio si basa su varargined evita tutti quegli infiniti e dolorosi if-then-elseo switchcasi con condizioni contorte. Quando ci sono alcuni valori predefiniti, l'effetto è drammatico . Ecco un esempio dal blog collegato:

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

Se ancora non lo capisci, prova a leggere l'intero post del blog di Loren. Ho scritto un post sul blog di follow-up che tratta dei valori predefiniti posizionali mancanti . Voglio dire che potresti scrivere qualcosa del tipo:

somefun2Alt(a, b, '', 42)

e ha ancora il epsvalore predefinito per il tolparametro (e @magiccallback per funcovviamente). Il codice di Loren lo consente con una leggera ma delicata modifica.

Infine, solo alcuni vantaggi di questo approccio:

  1. Anche con molte impostazioni predefinite il codice boilerplate non diventa enorme (al contrario della famiglia di if-then-elseapprocci, che si allunga con ogni nuovo valore predefinito)
  2. Tutte le impostazioni predefinite sono in un unico posto. Se qualcuno di questi ha bisogno di cambiare, hai solo un posto dove guardare.

A dire il vero, c'è anche uno svantaggio. Quando digiti la funzione nella shell Matlab e ne dimentichi i parametri, vedrai un vararginsuggerimento non utile. Per farvi fronte, vi consigliamo di scrivere una clausola d'uso significativa.


Il link al tuo post sul blog di follow-up è interrotto; Puoi aggiustarlo?
equaeghe

2

Dopo essere venuto a conoscenza di ASSIGNIN (grazie a questa risposta di b3 ) e EVALIN ho scritto due funzioni per ottenere finalmente una struttura di chiamata molto semplice:

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

Ecco la lista:

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

e

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

Questo è più o meno sollevato dal manuale di Matlab ; Ho solo esperienza di passaggio ...

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
Ci sono stati un paio di errori nel codice che ho corretto. Innanzitutto, è necessario definire "optargin". In secondo luogo, "varargin" è un array di celle che raccoglie tutti gli input successivi, quindi è necessario utilizzare l'indicizzazione dell'array di celle per rimuovere i valori da esso.
gnovice,

Devo controllare la vista; Giuro di non aver visto nulla di tutto ciò nel manuale di ieri :(
kyle

@kyle: non preoccuparti, tutti commettiamo errori. Ecco perché mi piace lo stile wiki di SO: se faccio qualche errore di battitura, di solito c'è qualcun altro in giro che può catturarlo e risolverlo rapidamente per me. =)
gnovice

1

Matlab non fornisce un meccanismo per questo, ma puoi costruirne uno nel codice userland che sia più terso delle sequenze inputParser o "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

Quindi puoi chiamarlo nelle tue funzioni in questo modo:

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 formattazione è una convenzione che consente di leggere dai nomi dei parametri ai loro valori predefiniti. È possibile estendere getargs () con specifiche del tipo di parametro facoltativo (per il rilevamento degli errori o la conversione implicita) e intervalli di conteggio degli argomenti.

Ci sono due svantaggi di questo approccio. Innanzitutto, è lento, quindi non si desidera utilizzarlo per le funzioni chiamate in loop. In secondo luogo, la guida delle funzioni di Matlab - i suggerimenti di completamento automatico sulla riga di comando - non funzionano per le funzioni varargin. Ma è abbastanza conveniente.


0

potresti voler usare il parseparamscomando in matlab; l'utilizzo sarebbe simile a:

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;

ad es. f(2,4,'c',3)fa sì che il parametro csia 3.


0

se usassi l'ottava potresti farlo in questo modo - ma purtroppo matlab non supporta questa possibilità

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

(tratto dal documento )


0

Mi piace farlo in un modo un po 'più orientato agli oggetti. Prima di chiamare wave () salva alcuni dei tuoi argomenti all'interno di una struttura, ad es. uno chiamato parametri:

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

All'interno della funzione d'onda, quindi, controlla se i parametri della struttura contengono un campo chiamato 'flag' e, in tal caso, se il suo valore non è vuoto. Quindi assegnagli un valore predefinito definito in precedenza, o il valore fornito come argomento nella struttura dei parametri:

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

Ciò semplifica la gestione di un numero elevato di argomenti, poiché non dipende dall'ordine degli argomenti forniti. Detto questo, è anche utile aggiungere più argomenti in seguito, poiché non è necessario modificare la firma delle funzioni per farlo.


Perché non seguire lo standard MATLAB delle coppie nome-valore? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo,

Anche questa è certamente un'opzione, specialmente quando si hanno solo pochi parametri. Chiamare wave () con un gran numero di coppie nome-valore, tuttavia, potrebbe ridurre la leggibilità del codice. Preferisco quindi spesso raggruppare determinati input in strutture.
CheshireCat,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.