Como definir valores padrão para parâmetros de funções no Matlab?

127

É possível ter argumentos padrão no Matlab? Por exemplo, aqui:

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

Eu gostaria que a solução verdadeira fosse um argumento opcional para a função de onda. Se for possível, alguém pode demonstrar a maneira correta de fazer isso? Atualmente, estou tentando o que postei acima e recebo:

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

Respostas:

151

Não há uma maneira direta de fazer isso como você tentou.

A abordagem usual é usar "varargs" e comparar com o número de argumentos. Algo como:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Existem algumas coisas mais interessantes que você pode fazer isempty, etc., e você pode querer olhar para o Matlab central para alguns pacotes que agrupam esse tipo de coisa.

Você pode ter uma olhada varargin, nargchketc. Eles são funções úteis para esse tipo de coisa. varargs permitem que você deixe um número variável de argumentos finais, mas isso não evita o problema dos valores padrão para alguns / todos eles.

simon
fonte
58

Eu usei o inputParserobjeto para lidar com a configuração de opções padrão. O Matlab não aceitará o formato de python especificado na pergunta, mas você poderá chamar a função assim:

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

Depois de definir a wavefunção assim:

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

Agora, os valores passados ​​para a função estão disponíveis i_p.Results. Além disso, eu não tinha certeza de como validar se o parâmetro passado ftrueera realmente uma inlinefunção, então deixei o validador em branco.

Matt
fonte
7
Como melhor eu posso dizer, este é o método preferido. É limpo, auto-documentado (mais um monte de if narginestatemens), fácil de manter, compacto e flexível.
JnBrymn
19

Outra maneira um pouco menos hacky é

function output = fun(input)
   if ~exist('input','var'), input='BlahBlahBlah'; end
   ...
end
Peter
fonte
Esta opção não funciona se você usar o codificador MATLAB para gerar o código C, pois o codificador não suporta a função "existir".
Dave Tillman
10

Sim, pode ser muito bom ter a capacidade de fazer o que você escreveu. Mas isso não é possível no MATLAB. Muitos dos meus utilitários que permitem padrões para os argumentos tendem a ser escritos com verificações explícitas no início, assim:

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, então eu geralmente aplicaria uma mensagem de erro melhor e mais descritiva. Veja que a verificação de uma variável vazia permite que o usuário passe um par de colchetes vazios, [], como um espaço reservado para uma variável que assumirá seu valor padrão. O autor ainda deve fornecer o código para substituir esse argumento vazio por seu valor padrão.

Meus utilitários mais sofisticados, com MUITOS parâmetros, todos com argumentos padrão, geralmente usam uma interface de par de propriedade / valor para argumentos padrão. Esse paradigma básico é visto nas ferramentas gráficas de manipulação no matlab, assim como no optimset, odeset, etc.

Como forma de trabalhar com esses pares de propriedade / valor, você precisará aprender sobre o varargin, como uma maneira de inserir um número totalmente variável de argumentos em uma função. Escrevi (e publiquei) um utilitário para trabalhar com esses pares de propriedade / valor, parse_pv_pairs.m . Ele ajuda a converter pares de propriedade / valor em uma estrutura do matlab. Ele também permite que você forneça valores padrão para cada parâmetro. Converter uma lista difícil de parâmetros em uma estrutura é uma maneira MUITO legal de distribuí-los no MATLAB.


fonte
7

Esta é a minha maneira simples de definir valores padrão para uma função, usando "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

Saudações!

Jonay Cruz
fonte
3

Descobri que a função parseArgs pode ser muito útil.

Mr Fooz
fonte
3

Também existe um 'hack' que pode ser usado, embora possa ser removido do matlab em algum momento: A função eval realmente aceita dois argumentos, dos quais o segundo é executado se ocorrer um erro no primeiro.

Assim, podemos usar

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

usar o valor 1 como padrão para o argumento

vuakko
fonte
3

Acredito que encontrei uma maneira bacana de lidar com esse problema, ocupando apenas três linhas de código (barramento de linhas). O seguinte é retirado diretamente de uma função que estou escrevendo e parece funcionar como desejado:

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

Só pensei em compartilhar.

Bonnevie
fonte
3

Estou confuso que ninguém apontou este post de Loren, um dos desenvolvedores do Matlab. A abordagem é baseada varargine evita todos aqueles casos intermináveis ​​e dolorosos if-then-elseou switchcom condições complicadas. Quando existem alguns valores padrão, o efeito é dramático . Aqui está um exemplo do blog vinculado:

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 você ainda não entendeu, tente ler toda a postagem de Loren. Eu escrevi uma postagem de blog de acompanhamento que lida com a falta de valores padrão posicionais. Quero dizer que você poderia escrever algo como:

somefun2Alt(a, b, '', 42)

e ainda tem o epsvalor padrão para o tolparâmetro (e @magicretorno de chamada, é funcclaro). O código de Loren permite isso com uma modificação leve, porém complicada.

Por fim, apenas algumas vantagens dessa abordagem:

  1. Mesmo com muitos padrões, o código padrão não fica grande (ao contrário da família de if-then-elseabordagens, que fica mais longa a cada novo valor padrão)
  2. Todos os padrões estão em um só lugar. Se algum desses precisar mudar, você tem apenas um lugar para procurar.

Trooth seja informado, também há uma desvantagem. Quando você digita a função no shell do Matlab e esquece seus parâmetros, verá uma varargindica inútil . Para lidar com isso, é recomendável escrever uma cláusula de uso significativa.

alisianoi
fonte
O link para sua postagem de blog de acompanhamento está quebrado; consegues consertar isso?
precisa saber é o seguinte
2

Depois de tomar conhecimento de ASSIGNIN (graças a esta resposta de b3 ) e EVALIN , escrevi duas funções para finalmente obter uma estrutura de chamada muito simples:

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

Aqui está a 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 ')']) ;
Tobias Kienzler
fonte
1

Isso é mais ou menos retirado do manual do Matlab ; Eu só tenho experiência de passar ...

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
fonte
1
Houve alguns erros no código que eu corrigi. Primeiro, "optgin" precisa ser definido. Segundo, "varargin" é uma matriz de células que coleta todas as entradas subseqüentes; portanto, você precisa usar a indexação da matriz de células para remover valores dela.
Gnovice 28/04/09
Eu preciso ter minha visão verificada; Juro que vi nada disso ontem no manual do :(
kyle
@kyle: Não se preocupe, todos cometemos erros. É por isso que eu gosto do estilo wiki-ish do SO: se eu faço algum erro bobo, geralmente há alguém por perto que pode pegá-lo e corrigi-lo rapidamente para mim. =)
gnovice 29/04
1

O Matlab não fornece um mecanismo para isso, mas você pode construir um no código da terra do usuário que é mais tenso do que as seqüências 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

Então você pode chamá-lo em suas funções como esta:

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

A formatação é uma convenção que permite a leitura de nomes de parâmetros para seus valores padrão. Você pode estender seu getargs () com especificações opcionais de tipo de parâmetro (para detecção de erros ou conversão implícita) e intervalos de contagem de argumentos.

Existem duas desvantagens nessa abordagem. Primeiro, é lento, então você não deseja usá-lo para funções chamadas em loops. Segundo, a ajuda das funções do Matlab - as dicas de preenchimento automático na linha de comando - não funcionam para funções varargin. Mas é bastante conveniente.

Andrew Janke
fonte
0

você pode querer usar o parseparamscomando no matlab; o uso seria semelhante 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;
shabbychef
fonte
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;

por exemplo, f(2,4,'c',3)faz com que o parâmetro cseja 3.

Tobias Kienzler
fonte
0

se você usasse a oitava, poderia fazê-lo assim - mas, infelizmente, o matlab não suporta essa possibilidade

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

(retirado do documento )

wuschLOR
fonte
0

Eu gosto de fazer isso de uma maneira um pouco mais orientada a objetos. Antes de chamar wave (), salve alguns argumentos dentro de uma estrutura, por exemplo. um chamado parâmetros:

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

Dentro da função wave, verifique se os parâmetros struct contêm um campo chamado 'flag' e, se houver, se o valor não está vazio. Em seguida, atribua a ele um valor padrão definido anteriormente, ou o valor fornecido como argumento na estrutura de parâmetros:

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

Isso facilita o manuseio de um grande número de argumentos, porque não depende da ordem dos argumentos fornecidos. Dito isto, também é útil se você precisar adicionar mais argumentos posteriormente, porque não precisará alterar a assinatura das funções para fazer isso.

Gato de Cheshire
fonte
Por que não seguir o padrão MATLAB de pares nome-valor? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo
Essa também é certamente uma opção, especialmente quando se tem apenas alguns parâmetros. Chamar wave () com um grande número de pares nome-valor, no entanto, pode reduzir a legibilidade do código. Por isso, muitas vezes prefiro agrupar certas entradas em estruturas.
Gato de Cheshire