¿Cómo configuro los valores predeterminados para los parámetros de funciones en Matlab?


127

¿Es posible tener argumentos predeterminados en Matlab? Por ejemplo, aquí:

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

Me gustaría que la verdadera solución sea un argumento opcional para la función de onda. Si es posible, ¿alguien puede demostrar la forma correcta de hacer esto? Actualmente, estoy intentando lo que publiqué anteriormente y obtengo:

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

Respuestas:


151

No hay una forma directa de hacer esto como lo has intentado.

El enfoque habitual es usar "varargs" y verificar el número de argumentos. Algo como:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Hay algunas cosas más sofisticadas con las que puede hacer isempty, etc., y es posible que desee consultar Matlab central para ver algunos paquetes que agrupan este tipo de cosas.

Es posible echar un vistazo a varargin, nargchk, etc. Son funciones útiles para este tipo de cosas. Los varargs le permiten dejar un número variable de argumentos finales, pero esto no le ayuda a resolver el problema de los valores predeterminados para algunos / todos ellos.


58

He usado el inputParserobjeto para tratar de configurar las opciones predeterminadas. Matlab no aceptará el formato similar a python que especificó en la pregunta, pero debería poder llamar a la función de esta manera:

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

Después de definir la wavefunción de esta manera:

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

Ahora los valores pasados ​​a la función están disponibles a través de i_p.Results. Además, no estaba seguro de cómo validar que el parámetro pasado ftrueera en realidad una inlinefunción, así que dejé el validador en blanco.


77
Lo mejor que puedo decir es que este es el método preferido. Es limpio, autodocumentado (más que un montón de if narginestadios), fácil de mantener, compacto y flexible.
JnBrymn

19

Otra forma un poco menos hacky es

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

Esta opción no funciona si va a utilizar MATLAB Coder para generar código C, ya que Coder no admite la función "existe".
Dave Tillman

10

Sí, podría ser realmente bueno tener la capacidad de hacer lo que ha escrito. Pero no es posible en MATLAB. Muchas de mis utilidades que permiten valores predeterminados para los argumentos tienden a escribirse con comprobaciones explícitas al principio de esta manera:

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, entonces generalmente aplicaría un mensaje de error mejor y más descriptivo. Observe que la comprobación de una variable vacía permite al usuario pasar un par de paréntesis vacíos, [], como marcador de posición para una variable que tomará su valor predeterminado. Sin embargo, el autor aún debe proporcionar el código para reemplazar ese argumento vacío con su valor predeterminado.

Mis utilidades que son más sofisticadas, con MUCHOS parámetros, todos los cuales tienen argumentos predeterminados, a menudo usan una interfaz de par propiedad / valor para los argumentos predeterminados. Este paradigma básico se ve en las herramientas de manejo de gráficos en matlab, así como en optimset, odeset, etc.

Como un medio para trabajar con estos pares de propiedad / valor, necesitará aprender sobre varargin, como una forma de ingresar un número completamente variable de argumentos a una función. Escribí (y publiqué) una utilidad para trabajar con tales pares de propiedad / valor, parse_pv_pairs.m . Le ayuda a convertir pares de propiedad / valor en una estructura matlab. También le permite proporcionar valores predeterminados para cada parámetro. Convertir una lista de parámetros difícil de manejar en una estructura es una manera MUY buena de pasarlos en MATLAB.


7

Esta es mi manera simple de establecer valores predeterminados para una función, 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

¡Saludos!



3

También hay un 'hack' que puede usarse, aunque podría eliminarse de matlab en algún momento: la función eval realmente acepta dos argumentos, de los cuales el segundo se ejecuta si ocurre un error con el primero.

Así podemos usar

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

usar el valor 1 como predeterminado para el argumento


3

Creo que encontré una manera bastante ingeniosa de lidiar con este problema, tomando solo tres líneas de código (excluyendo los ajustes de línea). Lo siguiente se extrae directamente de una función que estoy escribiendo, y parece funcionar como se desea:

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

Solo pensé en compartirlo.


3

Estoy confundido de que nadie haya señalado esta publicación de blog de Loren, uno de los desarrolladores de Matlab. El enfoque se basa vararginy evita todos esos casos interminables y dolorosos if-then-elseo switchcon condiciones complicadas. Cuando hay algunos valores predeterminados, el efecto es dramático . Aquí hay un ejemplo del 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{:};

Si aún no lo entiende, intente leer toda la publicación de blog de Loren. He escrito una publicación de blog de seguimiento que trata con valores predeterminados posicionales faltantes . Quiero decir que podrías escribir algo como:

somefun2Alt(a, b, '', 42)

y todavía tiene el epsvalor predeterminado para el tolparámetro (y la @magicdevolución de llamada, por funcsupuesto). El código de Loren permite esto con una modificación leve pero complicada.

Finalmente, solo algunas ventajas de este enfoque:

  1. Incluso con muchos valores predeterminados, el código repetitivo no se vuelve enorme (a diferencia de la familia de if-then-elseenfoques, que se alarga con cada nuevo valor predeterminado)
  2. Todos los valores predeterminados están en un solo lugar. Si alguno de esos necesita cambiar, solo tiene un lugar para mirar.

A pesar de todo, también hay una desventaja. Cuando escribe la función en el shell de Matlab y olvida sus parámetros, verá un poco útil varargincomo una pista. Para lidiar con eso, se recomienda escribir una cláusula de uso significativa.


El enlace a su publicación de blog de seguimiento está roto; ¿puedes arreglarlo?
Equaeghe

2

Después de conocer ASSIGNIN (gracias a esta respuesta de b3 ) y EVALIN , escribí dos funciones para finalmente obtener una estructura de llamada muy simple:

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

Aquí está el listado:

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

y

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

Esto es más o menos sacado del manual de Matlab ; Solo tengo experiencia pasajera ...

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
Hubo un par de errores en el código que corregí. Primero, "optargin" necesita ser definido. En segundo lugar, "varargin" es una matriz de celdas que recopila todas las entradas posteriores, por lo que debe usar la indexación de la matriz de celdas para eliminar los valores.
gnovice

Necesito controlar mi visión; Juro que no vi nada de eso en el manual ayer :(
kyle

@kyle: No te preocupes, todos cometemos errores. Es por eso que me gusta el estilo wiki-ish de SO: si hago un error tipográfico tonto, generalmente hay alguien más que puede atraparlo y solucionarlo rápidamente. =)
gnovice

1

Matlab no proporciona un mecanismo para esto, pero puede construir uno en el código de usuario que sea más terso que las secuencias 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

Entonces puedes llamarlo en tus funciones así:

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

El formato es una convención que le permite leer los nombres de los parámetros a sus valores predeterminados. Puede ampliar sus getargs () con especificaciones de tipo de parámetro opcionales (para detección de errores o conversión implícita) y rangos de recuento de argumentos.

Hay dos inconvenientes para este enfoque. Primero, es lento, por lo que no desea usarlo para funciones que se llaman en bucles. En segundo lugar, la ayuda de funciones de Matlab, las sugerencias de autocompletado en la línea de comandos, no funcionan para las funciones de varargin. Pero es bastante conveniente.


0

es posible que desee utilizar el parseparamscomando en matlab; el uso se vería así:

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;

Por ejemplo, f(2,4,'c',3)hace que el parámetro csea ​​3.


0

si usara octava, podría hacerlo así, pero lamentablemente matlab no admite esta posibilidad

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

(tomado del documento )


0

Me gusta hacer esto de una manera algo más orientada a objetos. Antes de llamar a wave (), guarde algunos de sus argumentos dentro de una estructura, por ejemplo. uno llamado parámetros:

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

Dentro de la función de onda, verifique si los parámetros de estructura contienen un campo llamado 'flag' y, si es así, si su valor no está vacío. Luego, asigne un valor predeterminado que defina antes, o el valor dado como argumento en la estructura 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

Esto facilita el manejo de un gran número de argumentos, ya que no depende del orden de los argumentos dados. Dicho esto, también es útil si tiene que agregar más argumentos más adelante, porque no tiene que cambiar la firma de funciones para hacerlo.


¿Por qué no seguir el estándar MATLAB de pares nombre-valor? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo

Esta también es una opción, especialmente cuando uno tiene pocos parámetros. Sin embargo, llamar a wave () con una gran cantidad de pares de nombre-valor podría reducir la legibilidad del código. Por lo tanto, a menudo prefiero agrupar ciertas entradas en estructuras.
CheshireCat
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.