Esistono tipi enumerati in MATLAB? In caso contrario, quali sono le alternative?Come posso creare tipi enumerati in MATLAB?
risposta
è possibile ottenere alcune delle funzionalità con le classi MATLAB nuovo stile:
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
questo non è davvero un tipo, ma dal momento che MATLAB è liberamente digitato, se si utilizza numeri interi, si possono fare cose che approssimarla:
line1 = Colors.RED;
...
if Colors.BLUE == line1
end
In questo caso, MATLAB "enumerazioni" sono vicini a enumerazioni in stile C - sintassi sostituto per gli interi.
Con l'uso attento dei metodi statici, è possibile anche fare in modo che le enigmi di MATLAB si avvicinino alla complessità di Ada, ma sfortunatamente con una sintassi più impacciata.
Se si vuole fare qualcosa di simile a quello che Marc suggerito, si potrebbe semplicemente fare una structure per rappresentare i vostri tipi enumerati, invece di un'intera classe nuova:
colors = struct('RED',1,'GREEN',2,'BLUE',3);
Un vantaggio è che si può facilmente accedere alle strutture in due modi diversi. È possibile specificare un campo direttamente utilizzando il nome del campo:
a = colors.RED;
oppure è possibile utilizzare dynamic field names se avete il nome del campo in una stringa:
a = colors.('RED');
In realtà, ci sono alcuni vantaggi a fare cosa Marc ha suggerito e creando una classe completamente nuova per rappresentare un oggetto "enum":
- È possibile controllare come l'oggetto viene modificato.
- È possibile mantenere la definizione in un unico punto e utilizzarla facilmente in più posizioni.
- È possibile controllare i guasti e renderli più "aggraziati", come restituire una matrice vuota se si tenta di accedere a un campo inesistente (al contrario di generare un errore).
Tuttavia, se non si ha bisogno di quel tipo di complessità e basta fare qualcosa in fretta, una struttura è probabilmente l'implementazione più semplice e diretta. Funzionerà anche con le versioni precedenti di MATLAB che non utilizzano il più recente framework OOP.
Se si ha accesso a Strumenti di statistica, è possibile prendere in considerazione l'utilizzo di categorical object.
È possibile creare una classe Matlab che si comporta come un Java's old typesafe enum pattern. Una modifica di Marc's solution potrebbe derivare da typedef in stile C a più simili enumerazioni in stile typesafe in stile Java. In questa versione, i valori nelle costanti sono digitati Oggetti colore.
I pregi:
- Il tipo può essere controllata (in fase di esecuzione) di == e altre operazioni per evitare accidentali confronto al numerici elaborati o altri tipi di enumerazioni.
- È possibile verificare esplicitamente il tipo di variabili (in fase di esecuzione).
- I valori sono visualizzati con nomi leggibili anziché i codici opachi.
- Operazioni come mean() e std() che non hanno senso sulle enumerazioni non sono consentite.
Svantaggi:
- Longer definizione di classe. Ma questo è tutto il boilerplate e può essere riutilizzato per qualsiasi altra classe enum, cambiando solo il nome della classe e le proprietà Constant.
- Queste enumerazioni non possono essere utilizzate direttamente nei blocchi di interruttori. È necessario far uscire il codice, che perde un po 'di sicurezza.
- Gli oggetti saranno più lenti dei primitivi. Rilevante se si utilizzano le costanti all'interno dei loop.
Nel complesso, non so quale approccio sia migliore. Non ho usato neanche nella pratica.
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
Ecco una funzione per esercitarlo.
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
Esempio di utilizzo:
>> 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
>>
Un cavillo minore in entrambi gli approcci: la convenzione C di mettere costante sulla mano sinistra del "==" per impedire cattiva assegnazione non aiuta tanto qui. In Matlab, se si utilizza accidentalmente "=" con questa costante sul LHS, invece di un errore, verrà semplicemente creata una nuova variabile di struttura locale denominata Colori e verrà mascherata la classe enum.
>> Colors.BLUE = 42
Colors =
BLUE: 42
>> Color.BLUE = 42
Color =
BLUE: 42
>> Color.RED
??? Reference to non-existent field 'RED'.
Molto carino, Andrew. Mi chiedevo quando avresti guadato. – Marc
È anche possibile utilizzare classi di enumerazione Java dal codice Matlab. Definiscili in Java e inseriscili nel javaclasspath di Matlab.
// Java class definition
package test;
public enum ColorEnum {
RED, GREEN, BLUE
}
È possibile fare riferimento per nome in codice M.
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
Tuttavia, non prenderà confronti con altri tipi. E il confronto con la stringa ha una dimensione di ritorno dispari.
>> test.ColorEnum.RED == 'GREEN'
ans =
0
>> test.ColorEnum.RED == 'RED'
ans =
1 1 1
V'è in realtà una parola chiave in MATLAB R2009b chiamato 'enumerazione'. Sembra non documentato, e non posso dire di sapere come usarlo, ma probabilmente c'è la funzionalità.
Lo si può trovare in matlabroot\toolbox\distcomp\examples\+examples
classdef(Enumeration) DmatFileMode < int32
enumeration
ReadMode(0)
ReadCompatibilityMode(1)
WriteMode(2)
end
<snip>
end
Questo è il modo corretto di farlo se si eseguirà la generazione del codice. È documentato meglio nella documentazione di Simulink in "Definizione di un tipo di dati enumerati". –
Dopo aver provato gli altri suggerimenti in questa pagina, sono atterrato su un approccio completamente orientato agli oggetti di Andrew. Molto carino - grazie Andrew.
Nel caso in cui qualcuno sia interessato, tuttavia, ho apportato alcuni miglioramenti (ciò che penso). In particolare, ho rimosso la necessità di specificare due volte il nome dell'oggetto enum. I nomi sono ora derivati utilizzando il reflection e il sistema metaclass. Inoltre, le funzioni eq() e ismember() sono state riscritte per restituire valori di ritorno opportunamente sagomati per matrici di oggetti enum. Infine, la funzione check_type_safety() è stata modificata per renderla compatibile con le directory dei pacchetti (ad esempio namespace).
sembra funzionare bene, ma fatemi sapere cosa ne pensate:
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
Grazie, Mason
A partire da R2010b, MATLAB supporta enumerazioni.
Esempio dal 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
per i futuri lettori, questa probabilmente dovrebbe essere la risposta accettata (tutte le buone soluzioni comunque) – Amro
Questo è un esempio più complicato della classe enum. È possibile trovare uno più semplice all'interno della documentazione. – KronoS
Url per l'esempio più semplice @KronoS menzionato: [link] (http://www.mathworks.de/de/help/matlab/matlab_oop/enumerations.html) – JaBe
Se avete bisogno dei tipi enumerati solo per il passaggio a C# o assembly .NET, è possibile costruire e trasmettere le enumerazioni con MATLAB 2010:
A = NET.addAssembly(MyName.dll)
% suppose you have enum called "MyAlerts" in your assembly
myvar = MyName.MyAlerts.('value_1');
puoi anche verificare la risposta ufficiale di MathWorks al
// the enum "MyAlerts" in c# will look something like this
public enum MyAlerts
{
value_1 = 0,
value_2 = 1,
MyAlerts_Count = 2,
}
Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};
Toys{3}
ans = 'Rex'
Basta essere consapevoli dei possibili colpi di prestazioni quando si utilizza il nuovo materiale orientato agli oggetti. Nella mia esperienza, ha introdotto un sovraccarico significativo. Dipende davvero da cosa stai facendo, comunque. –
In realtà, per le classi semplici, non vi è effettivamente alcuna penalità di tempo rispetto all'utilizzo di molte strutture globali. Tuttavia, i risparmi in termini di tempo di sviluppo per le buone pratiche sono notevoli e raramente questo è un problema di runtime. L'hardware è economico. Le persone che scrivono il software non lo sono. – Marc