tag - Argumentos predeterminados en Matlab
title matlab (16)
¿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 hacerlo? 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.
¡Gracias!
Creo que encontré una forma bastante ingeniosa de manejar este problema, ocupando solo tres líneas de código (excepto las líneas bloqueadas). Lo siguiente se elimina 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.
Descubrí que la función parseArgs puede ser muy útil.
Después de tomar consciencia de ASSIGNIN (gracias a esta respuesta por b3 ) y EVALIN escribí dos funciones para finalmente obtener una estructura de llamadas muy simple:
setParameterDefault(''fTrue'', inline(''0''));
Aquí está la lista:
function setParameterDefault(pname, defval)
% setParameterDefault(pname, defval)
% Author: Tobias Kienzler (https://.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://.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 '')'']) ;
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!
Esto está más o menos levantado 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
Estoy confundido, nadie ha señalado esta publicación del blog de Loren, uno de los desarrolladores de Matlab. El enfoque se basa en varargin
y evita todos esos interminables y dolorosos if-then-else
o switch
caso con condiciones intrincadas. Cuando hay unos pocos 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 obtiene, intente leer la publicación completa del blog de Loren. He escrito una publicación de seguimiento del blog que trata sobre los valores predeterminados de posición faltantes . Quiero decir que podrías escribir algo como:
somefun2Alt(a, b, '''', 42)
y todavía tienen el valor de eps
predeterminado para el parámetro de tol
(y la devolución de llamada de @magic
por supuesto, por supuesto). El código de Loren permite esto con una ligera pero complicada modificación.
Finalmente, solo unas pocas ventajas de este enfoque:
- Incluso con muchos incumplimientos, el código repetitivo no se vuelve enorme (a diferencia de la familia de enfoques
if-then-else
, que se hacen más largos con cada nuevo valor predeterminado) - Todos los valores predeterminados están en un solo lugar. Si alguno de ellos necesita cambiar, solo tiene un lugar para mirar.
Trooth sea dicho, también hay una desventaja. Cuando escribe la función en el shell de Matlab y olvida sus parámetros, verá un varargin
inútil como una pista. Para lidiar con eso, se recomienda escribir una cláusula de uso significativo.
Hasta donde yo sé, no hay una forma directa de hacer esto como lo has intentado.
El enfoque habitual es usar varargs y verificar contra el número de args. Algo como:
function f(arg1,arg2,arg3)
if nargin < 3
arg3 = ''some default''
end
end
Hay algunas cosas más elegantes que puedes hacer con isempty, etc., y es posible que desees consultar matlab central para algunos paquetes que combinan este tipo de cosas.
[actualización] contento de que ayudó.
puedes echarle un vistazo a varargin, nargchk, etc. son funciones útiles para este tipo de cosas. Los varargs le permiten dejar una cantidad variable de argumentos finales, pero esto no le ayuda a resolver el problema de los valores predeterminados para algunos / todos ellos.
He usado el objeto inputParser
para tratar de establecer las opciones predeterminadas. Matlab no aceptará el formato tipo pitón 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 función de wave
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 por ftrue
era en realidad una función en inline
, por lo que dejó el validador en blanco.
Matlab no proporciona un mecanismo para esto, pero puede construir uno en el código de usuario que sea más terser que inputParser o secuencias "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 puede llamarlo en sus funciones de esta manera:
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 desde 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 en este enfoque. En primer lugar, es lento, por lo que no desea utilizarlo para las funciones que se llaman en bucles. En segundo lugar, la función de ayuda de Matlab - las sugerencias de autocompletado en la línea de comando - no funcionan para las funciones varargin. Pero es bastante conveniente.
Me gusta hacer esto de una manera algo más orientada a objetos. Antes de llamar a wave () guarda algunos de tus argumentos dentro de una estructura, ej. 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, luego verifique si los parámetros de estructura contienen un campo llamado ''bandera'' y, de ser así, si su valor no está vacío. Luego, asígnele un valor predeterminado que defina antes, o el valor dado como argumento en los parámetros struct:
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 debe agregar más argumentos más adelante, porque no tiene que cambiar la firma de las funciones para hacerlo.
Otra forma un poco menos hacky es
function output = fun(input)
if ~exist(''input'',''var''), input=''BlahBlahBlah''; end
...
end
Sí, podría ser realmente bueno tener la capacidad de hacer lo que usted ha escrito. Pero no es posible en MATLAB. Muchas de mis utilidades que permiten los valores predeterminados de los argumentos tienden a escribirse con comprobaciones explícitas al principio como esta:
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 le permite al usuario pasar un par de corchetes vacíos, [], como marcador de posición para una variable que tomará su valor predeterminado. Sin embargo, el autor debe suministrar 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 usarán una interfaz de paridad 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, tendrá que aprender sobre varargin, como una forma de ingresar una cantidad totalmente variable de argumentos a una función. Escribí (y publiqué) una utilidad para trabajar con dichos pares de propiedad / valor, parse_pv_pairs.m . Le ayuda a convertir pares de propiedad / valor en una estructura de matlab. También le permite suministrar valores predeterminados para cada parámetro. Convertir una lista inmanejable de parámetros en una estructura es una manera MUY agradable de pasarlos en MATLAB.
También hay un ''hack'' que puede usarse aunque se elimine de matlab en algún momento: Function eval acepta realmente dos argumentos, de los cuales el segundo se ejecuta si ocurre un error con el primero.
Por lo tanto, podemos usar
function output = fun(input)
eval(''input;'', ''input = 1;'');
...
end
para usar el valor 1 como predeterminado para el argumento
es posible que desee utilizar el comando parseparams
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;
si usas la octava, puedes hacerlo así, pero lamentablemente el matlab no admite esta posibilidad
function hello (who = "World")
printf ("Hello, %s!/n", who);
endfunction
(tomado del doc )
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;
eg f(2,4,''c'',3)
hace que el parámetro c
sea 3.