¿Cómo creo tipos enumerados en MATLAB?
enums matlab-class (10)
¿Hay tipos enumerados en MATLAB? si no, cuales son las alternativas?
A partir de R2010b, MATLAB admite enumeraciones.
Ejemplo de la documentation :
classdef Colors
properties
R = 0;
G = 0;
B = 0;
end
methods
function c = Colors(r, g, b)
c.R = r; c.G = g; c.B = b;
end
end
enumeration
Red (1, 0, 0)
Green (0, 1, 0)
Blue (0, 0, 1)
end
end
Después de probar las otras sugerencias en esta página, llegué al enfoque totalmente orientado a objetos de Andrew. Muy bien, gracias Andrew.
En caso de que alguien esté interesado, sin embargo, hice (lo que creo que son) algunas mejoras. En particular, eliminé la necesidad de especificar dos veces el nombre del objeto enum. Los nombres ahora se derivan usando la reflexión y el sistema de metaclase. Además, las funciones eq () y ismember () se volvieron a escribir para devolver valores de retorno con la forma adecuada para matrices de objetos enum. Y finalmente, se modificó la función check_type_safety () para que sea compatible con los directorios de paquetes (por ejemplo, espacios de nombres).
Parece que funciona bien, pero déjame saber lo que piensas:
classdef (Sealed) Color
%COLOR Example of Java-style typesafe enum for Matlab
properties (Constant)
RED = Color(1);
GREEN = Color(2);
BLUE = Color(3);
end
methods (Access = private) % private so that you can''''t instatiate directly
function out = Color(InCode)
out.Code = InCode;
end
end
% ============================================================================
% Everything from here down is completely boilerplate - no need to change anything.
% ============================================================================
properties (SetAccess=private) % All these properties are immutable.
Code;
end
properties (Dependent, SetAccess=private)
Name;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
methods
function out = eq(a, b) %EQ Basic "type-safe" eq
check_type_safety(a, b);
out = reshape([a.Code],size(a)) == reshape([b.Code],size(b));
end
function [tf,loc] = ismember(a, b)
check_type_safety(a, b);
[tf,loc] = ismember(reshape([a.Code],size(a)), [b.Code]);
end
function check_type_safety(varargin) %CHECK_TYPE_SAFETY Check that all inputs are of this enum type
theClass = class(varargin{1});
for ii = 2:nargin
if ~isa(varargin{ii}, theClass)
error(''Non-typesafe comparison of %s vs. %s'', theClass, class(varargin{ii}));
end
end
end
% Display stuff:
function display(obj)
disp([inputname(1) '' ='']);
disp(obj);
end
function disp(obj)
if isscalar(obj)
fprintf(''%s: %s (%d)/n'', class(obj), obj.Name, obj.Code);
else
fprintf(''%s array: size %s/n'', class(obj), mat2str(size(obj)));
end
end
function name=get.Name(obj)
mc=metaclass(obj);
mp=mc.Properties;
for ii=1:length(mp)
if (mp{ii}.Constant && isequal(obj.(mp{ii}.Name).Code,obj.Code))
name = mp{ii}.Name;
return;
end;
end;
error(''Unable to find a %s value of %d'',class(obj),obj.Code);
end;
end
end
Gracias, Mason
En realidad, hay una palabra clave en MATLAB R2009b llamada ''enumeración'' . Parece que no está documentado, y no puedo decir que sé cómo usarlo, pero la funcionalidad probablemente esté allí.
Puede encontrarlo en matlabroot/toolbox/distcomp/examples/+examples
classdef(Enumeration) DmatFileMode < int32
enumeration
ReadMode(0)
ReadCompatibilityMode(1)
WriteMode(2)
end
<snip>
end
Podrías crear una clase de Matlab que se comporte como un antiguo modelo de enumeración seguro de Java . Una modificación de la solución de Marc podría llevarlo desde los typedefs tipo C a más como enum de tipo tipo Java. En esta versión, los valores en las constantes son objetos de Color tipeados.
Las ventajas:
- El tipo puede ser verificado (en tiempo de ejecución) por == y otras operaciones para evitar la comparación accidental con los valores numéricos en bruto u otros tipos de enumeraciones.
- Puede verificar explícitamente el tipo de sus variables (en tiempo de ejecución).
- Los valores se muestran con nombres legibles en lugar de los códigos opacos.
- Las operaciones como mean () y std () que no tienen sentido en las enumeraciones no están permitidas.
Desventajas:
- Definición de clase más larga. Pero, esto es todo repetitivo, y puede reutilizarse para cualquier otra clase enum, cambiando solo el nombre de la clase y las propiedades de Constante.
- Estas enumeraciones no se pueden usar directamente en bloques de interruptores. Necesitas sacar el código, lo que pierde algún tipo de seguridad.
- Los objetos serán más lentos que los primitivos. Pertinente si está usando constantes dentro de los bucles.
En general, no sé qué enfoque es mejor. No lo he usado en la práctica.
classdef (Sealed) Color
%COLOR Example of Java-style typesafe enum for Matlab
properties (Constant)
RED = Color(1, ''RED'');
GREEN = Color(2, ''GREEN'');
BLUE = Color(3, ''BLUE'');
end
properties (SetAccess=private)
% All these properties are immutable.
Code;
Name;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
methods (Access = private)
%private so that you can''t instatiate directly
function out = Color(InCode, InName)
out.Code = InCode;
out.Name = InName;
end
end
methods (Static = true)
function needa(obj)
%NEEDA Asserts that obj must be a Color
if ~isa(obj, mfilename)
error(''Input must be a %s; got a %s'', mfilename, class(obj));
end
end
end
methods (Access = public)
function display(obj)
disp([inputname(1) '' ='']);
disp(obj);
end
function disp(obj)
if isscalar(obj)
disp(sprintf(''%s: %s (%d)'', class(obj), obj.Name, obj.Code));
else
disp(sprintf(''%s array: size %s'', class(obj), mat2str(size(obj))));
end
end
function out = eq(a, b)
%EQ Basic "type-safe" eq
check_type_safety(a, b);
out = [a.Code] == [b.Code];
end
function [tf,loc] = ismember(a, b)
check_type_safety(a, b);
[tf,loc] = ismember([a.Code], [b.Code]);
end
function check_type_safety(varargin)
%CHECK_TYPE_SAFETY Check that all inputs are of this enum type
for i = 1:nargin
if ~isa(varargin{i}, mfilename)
error(''Non-typesafe comparison of %s vs. %s'', mfilename, class(varargin{i}));
end
end
end
end
end
Aquí hay una función para ejercitarlo.
function do_stuff_with_color(c)
%DO_STUFF_WITH_COLOR Demo use of the Color typesafe enum
Color.needa(c); % Make sure input was a color
if (c == Color.BLUE)
disp(''color was blue'');
else
disp(''color was not blue'');
end
% To work with switch statements, you have to explicitly pop the code out
switch c.Code
case Color.BLUE.Code
disp(''blue'');
otherwise
disp(sprintf(''some other color: %s'', c.Name));
end
Ejemplo de uso:
>> Color.RED == Color.RED
ans =
1
>> Color.RED == 1
??? Error using ==> Color>Color.check_type_safety at 55
Non-typesafe comparison of Color vs. double
Error in ==> Color>Color.eq at 44
check_type_safety(a, b);
>> do_stuff_with_color(Color.BLUE)
color was blue
blue
>> do_stuff_with_color(Color.GREEN)
color was not blue
some other color: GREEN
>> do_stuff_with_color(1+1) % oops - passing the wrong type, should error
??? Error using ==> Color>Color.needa at 26
Input must be a Color; got a double
Error in ==> do_stuff_with_color at 4
Color.needa(c); % Make sure input was a color
>>
Una peculiaridad menor en ambos enfoques: la convención C de poner la constante en la mano izquierda del "==" para evitar una mala asignación no ayuda mucho aquí. En Matlab, si accidentalmente usas "=" con esta constante en el LHS, en lugar de un error, creará una nueva variable de estructura local llamada Colors, y enmascarará la clase enum.
>> Colors.BLUE = 42
Colors =
BLUE: 42
>> Color.BLUE = 42
Color =
BLUE: 42
>> Color.RED
??? Reference to non-existent field ''RED''.
Puede obtener algunas de las funcionalidades con las nuevas clases de MATLAB:
classdef (Sealed) Colors
properties (Constant)
RED = 1;
GREEN = 2;
BLUE = 3;
end
methods (Access = private) % private so that you cant instantiate
function out = Colors
end
end
end
Esto no es realmente un tipo, pero como MATLAB está tipeado libremente, si usas números enteros, puedes hacer cosas que se aproximen a él:
line1 = Colors.RED;
...
if Colors.BLUE == line1
end
En este caso, las "enumeraciones" de MATLAB están cerca de enums de estilo C: sintaxis sustituta para enteros.
Con el uso cuidadoso de métodos estáticos, incluso puede hacer que las enumeraciones de MATLAB se acerquen a la sofisticación de Ada, pero desafortunadamente con una sintaxis más torpe.
Si desea hacer algo similar a lo sugerido por Marc , simplemente podría hacer una structure para representar sus tipos enumerados en lugar de una clase completamente nueva:
colors = struct(''RED'',1,''GREEN'',2,''BLUE'',3);
Un beneficio es que puede acceder fácilmente a las estructuras de dos maneras diferentes. Puede especificar un campo directamente usando el nombre del campo:
a = colors.RED;
o puede usar nombres de campo dinámicos si tiene el nombre del campo en una cadena:
a = colors.(''RED'');
En verdad, hay algunos beneficios de hacer lo que Marc sugirió y crear una clase completamente nueva para representar un objeto "enum":
- Puedes controlar cómo se modifica el objeto.
- Puede mantener la definición en un solo lugar y usarla fácilmente en múltiples lugares.
- Puede controlar las fallas y hacerlas más "elegantes", como devolver una matriz vacía si intenta acceder a un campo inexistente (en lugar de arrojar un error).
Sin embargo, si no necesita ese tipo de complejidad y solo necesita hacer algo rápido, una estructura es probablemente la implementación más sencilla y directa. También funcionará con versiones anteriores de MATLAB que no usan el marco de OOP más nuevo.
Si necesita los tipos enumerados solo para pasar al ensamblado C # o .NET, puede construir y pasar las enumeraciones con MATLAB 2010:
A = NET.addAssembly(MyName.dll)
% suppose you have enum called "MyAlerts" in your assembly
myvar = MyName.MyAlerts.(''value_1'');
también puede verificar la respuesta oficial de MathWorks en
¿Cómo uso los valores enumerados de .NET en MATLAB 7.8 (R2009a)?
// the enum "MyAlerts" in c# will look something like this
public enum MyAlerts
{
value_1 = 0,
value_2 = 1,
MyAlerts_Count = 2,
}
Si tiene acceso a Statistics Toolbox, puede considerar el uso de un objeto categórico .
También puede usar las clases enum de Java desde su código Matlab. Defínelos en Java y colóquelos en el javaclasspath de Matlab.
// Java class definition
package test;
public enum ColorEnum {
RED, GREEN, BLUE
}
Puede hacer referencia a ellos por su nombre en M-code.
mycolor = test.ColorEnum.RED
if mycolor == test.ColorEnum.RED
disp(''got red'');
else
disp(''got other color'');
end
% Use ordinal() to get a primitive you can use in a switch statement
switch mycolor.ordinal
case test.ColorEnum.BLUE.ordinal
disp(''blue'');
otherwise
disp(sprintf(''other color: %s'', char(mycolor.toString())))
end
Sin embargo, no tomará comparaciones con otros tipos. Y la comparación con la cadena tiene un tamaño de retorno impar.
>> test.ColorEnum.RED == ''GREEN''
ans =
0
>> test.ColorEnum.RED == ''RED''
ans =
1 1 1
Toys = {''Buzz'', ''Woody'', ''Rex'', ''Hamm''};
Toys{3}
ans = ''Rex''