comando - ¿Cómo recupero los nombres de los parámetros de función en matlab?
comando legend matlab (6)
inputname(argnum)
http://www.mathworks.com/help/techdoc/ref/inputname.html .
Además de analizar el archivo de función, ¿hay alguna manera de obtener los nombres de los argumentos de entrada y salida para una función en matlab?
Por ejemplo, dado el siguiente archivo de función:
divide.m
function [value, remain] = divide(left, right)
value = floor(left / right);
remain = left / right - value;
end
Desde fuera de la función, quiero obtener una matriz de argumentos de salida, aquí: [''value'', ''remain'']
, y de manera similar para los argumentos de entrada: [''left'', ''right'']
.
¿Hay alguna manera fácil de hacer esto en Matlab? Matlab por lo general parece apoyar la reflexión bastante bien.
EDITAR Antecedentes:
El objetivo de esto es presentar los parámetros de la función en una ventana para que el usuario ingrese. Estoy escribiendo un tipo de programa de procesamiento de señal, y las funciones para realizar operaciones en estas señales se almacenan en una subcarpeta. Ya tengo una lista y los nombres de cada función de la que el usuario puede seleccionar, pero algunas funciones requieren argumentos adicionales (por ejemplo, una función sin problemas puede tomar el tamaño de ventana como parámetro).
Por el momento, puedo agregar una nueva función a la subcarpeta que el programa encontrará, y el usuario puede seleccionarla para realizar una operación. Lo que me falta es que el usuario especifique los parámetros de entrada y salida, y aquí he topado con el obstáculo aquí porque no puedo encontrar los nombres de las funciones.
¿Has considerado usar contenedores de mapas?
Puedes escribir tus funciones en esta línea. . .
function [outMAP] = divide(inMAP)
outMAP = containers.Map();
outMAP(''value'') = floor(inMAP(''left'') / inMAP(''right''));
outMAP(''remain'') = inMAP(''left'') / inMAP(''right'') - outMAP(''value'');
end
... y llamarlos así ...
inMAP = containers.Map({''left'', ''right''}, {4, 5});
outMAP = divide(inMAP);
... y luego simplemente examinar los nombres de las variables usando la siguiente sintaxis ...
>> keys(inMAP)
ans =
''left'' ''right''
Cuando no puede obtener información de un lenguaje de programación sobre su contenido (por ejemplo, "reflexión"), tiene que salir del idioma.
Otro cartel sugirió "expresiones regulares", que siempre fallan cuando se aplican al análisis de programas reales porque las expresiones regulares no pueden analizar lenguajes libres de contexto.
Para hacer esto de manera confiable, necesita un analizador de lenguaje M real, que le dará acceso al árbol de análisis sintáctico. Entonces esto es bastante fácil.
Nuestro DMS Software Reengineering Toolkit tiene un analizador de lenguaje M disponible para ello, y podría hacerlo.
Esto va a ser muy difícil (leer: imposible) para funciones generales (piense en cosas como varargin, etc.). Además, en general, depender de nombres de variables como una forma de documentación podría ser ... no lo que desea. Voy a sugerir un enfoque diferente.
Como usted controla el programa, ¿qué hay de especificar cada módulo no solo con el archivo m, sino también con una entrada de tabla con información adicional? Puede documentar los parámetros adicionales, la función en sí misma, anotar cuando las opciones son booleanas y presentarlas como casillas de verificación, etc.
Ahora, ¿dónde poner esto? Sugeriría que la función principal m-file devuelva la estructura, como una especie de paso de carga del módulo, con un identificador de función que apunta a la subfunción (o función anidada) que hace el trabajo real. Esto preserva la configuración de un solo archivo que estoy seguro desea conservar, y hace una configuración mucho más configurable para sus módulos.
function module = divide_load()
module.fn = @my_divide;
module.name = ''Divide'';
module.description = ''Divide two signals'';
module.param(1).name = ''left'';
module.param(1).description = ''left signal'';
module.param(1).required_shape = ''columnvector'';
% Etc, etc.
function [value, remain] = my_divide(left, right)
value = floor(left / right);
remain = left / right - value;
end
end
Si su problema se limita al caso simple en el que desea analizar la línea de declaración de función de una función principal en un archivo (es decir, no tratará con funciones secundarias , funciones anidadas o anónimas ), puede extraer la entrada y los nombres de los argumentos de salida tal como aparecen en el archivo usando algunas operaciones de cadena estándar y expresiones regulares . La línea de declaración de función tiene un formato estándar, pero debe contabilizar algunas variaciones debido a:
- Varias cantidades de espacio en blanco o líneas en blanco,
- La presencia de comentarios de una línea o bloque , y
- Tener la declaración dividida en más de una línea .
(Resulta que explicar un comentario en bloque fue la parte más difícil ...)
He creado una función get_arg_names
que manejará todo lo anterior. Si le da una ruta al archivo de función, devolverá dos matrices de celdas que contienen sus cadenas de parámetros de entrada y salida (o matrices de celdas vacías si no hay ninguna). Tenga en cuenta que las funciones con listas de entrada o salida variables simplemente enumerarán ''varargin''
o ''varargout''
, respectivamente, para los nombres de las variables. Aquí está la función:
function [inputNames, outputNames] = get_arg_names(filePath)
%# Open the file:
fid = fopen(filePath);
%# Skip leading comments and empty lines:
defLine = '''';
while all(isspace(defLine))
defLine = strip_comments(fgets(fid));
end
%# Collect all lines if the definition is on multiple lines:
index = strfind(defLine, ''...'');
while ~isempty(index)
defLine = [defLine(1:index-1) strip_comments(fgets(fid))];
index = strfind(defLine, ''...'');
end
%# Close the file:
fclose(fid);
%# Create the regular expression to match:
matchStr = ''/s*function/s+'';
if any(defLine == ''='')
matchStr = strcat(matchStr, ''/[?(?<outArgs>[/w, ]*)/]?/s*=/s*'');
end
matchStr = strcat(matchStr, ''/w+/s*/(?(?<inArgs>[/w, ]*)/)?'');
%# Parse the definition line (case insensitive):
argStruct = regexpi(defLine, matchStr, ''names'');
%# Format the input argument names:
if isfield(argStruct, ''inArgs'') && ~isempty(argStruct.inArgs)
inputNames = strtrim(textscan(argStruct.inArgs, ''%s'', ...
''Delimiter'', '',''));
else
inputNames = {};
end
%# Format the output argument names:
if isfield(argStruct, ''outArgs'') && ~isempty(argStruct.outArgs)
outputNames = strtrim(textscan(argStruct.outArgs, ''%s'', ...
''Delimiter'', '',''));
else
outputNames = {};
end
%# Nested functions:
function str = strip_comments(str)
if strcmp(strtrim(str), ''%{'')
strip_comment_block;
str = strip_comments(fgets(fid));
else
str = strtok(['' '' str], ''%'');
end
end
function strip_comment_block
str = strtrim(fgets(fid));
while ~strcmp(str, ''%}'')
if strcmp(str, ''%{'')
strip_comment_block;
end
str = strtrim(fgets(fid));
end
end
end
MATLAB ofrece una forma de obtener información sobre los metadatos de la clase (utilizando el meta
paquete), sin embargo, esto solo está disponible para las clases OOP que no son funciones regulares.
Un truco es escribir una definición de clase sobre la marcha, que contiene la fuente de la función que le gustaría procesar, y dejar que MATLAB se ocupe del análisis sintáctico del código fuente (que puede ser complicado como se imaginaría: línea de definición de función) abarca múltiples líneas, comentarios antes de la definición real, etc.)
Entonces, el archivo temporal creado en su caso se vería así:
classdef SomeTempClassName
methods
function [value, remain] = divide(left, right)
%# ...
end
end
end
que luego se puede pasar a meta.class.fromName
para analizar metadatos ...
Aquí hay una implementación rápida y sucia de este truco:
function [inputNames,outputNames] = getArgNames(functionFile)
%# get some random file name
fname = tempname;
[~,fname] = fileparts(fname);
%# read input function content as string
str = fileread(which(functionFile));
%# build a class containing that function source, and write it to file
fid = fopen([fname ''.m''], ''w'');
fprintf(fid, ''classdef %s; methods;/n %s/n end; end'', fname, str);
fclose(fid);
%# terminating function definition with an end statement is not
%# always required, but now becomes required with classdef
missingEndErrMsg = ''An END might be missing, possibly matching CLASSDEF.'';
c = checkcode([fname ''.m'']); %# run mlint code analyzer on file
if ismember(missingEndErrMsg,{c.message})
% append "end" keyword to class file
str = fileread([fname ''.m'']);
fid = fopen([fname ''.m''], ''w'');
fprintf(fid, ''%s /n end'', str);
fclose(fid);
end
%# refresh path to force MATLAB to detect new class
rehash
%# introspection (deal with cases of nested/sub-function)
m = meta.class.fromName(fname);
idx = find(ismember({m.MethodList.Name},functionFile));
inputNames = m.MethodList(idx).InputNames;
outputNames = m.MethodList(idx).OutputNames;
%# delete temp file when done
delete([fname ''.m''])
end
y simplemente ejecuta como:
>> [in,out] = getArgNames(''divide'')
in =
''left''
''right''
out =
''value''
''remain''