【发布时间】:2010-11-26 05:15:22
【问题描述】:
MATLAB 中有枚举类型吗?如果没有,有什么替代方案?
【问题讨论】:
标签: matlab enums matlab-class
MATLAB 中有枚举类型吗?如果没有,有什么替代方案?
【问题讨论】:
标签: matlab enums matlab-class
从 R2010b 开始,MATLAB 支持枚举。
来自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
【讨论】:
您可以使用新型 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
这不是一个真正的类型,但由于 MATLAB 是松散类型的,如果你使用整数,你可以做一些近似它的事情:
line1 = Colors.RED;
...
if Colors.BLUE == line1
end
在这种情况下,MATLAB“枚举”接近于 C 风格的枚举 - 整数的替代语法。
通过谨慎使用静态方法,您甚至可以使 MATLAB 枚举在复杂性方面接近 Ada,但不幸的是语法更笨拙。
【讨论】:
如果您想做一些与Marc 建议的类似的事情,您可以简单地创建一个structure 来表示您的枚举类型,而不是一个全新的类:
colors = struct('RED', 1, 'GREEN', 2, 'BLUE', 3);
一个好处是您可以通过两种不同的方式轻松访问结构。可以直接使用字段名指定字段:
a = colors.RED;
或者,如果您在字符串中包含字段名称,则可以使用 dynamic field names:
a = colors.('RED');
事实上,按照 Marc 的建议创建一个全新的类来表示“枚举”对象有一些好处:
但是,如果您不需要那种复杂性并且只需要快速执行某项操作,那么结构可能是最简单、最直接的实现。它也适用于不使用最新 OOP 框架的旧版 MATLAB。
【讨论】:
其实在 MATLAB R2009b 中有一个关键字叫做'enumeration'。它似乎没有记录,我不能说我知道如何使用它,但功能可能就在那里。
你可以在matlabroot\toolbox\distcomp\examples\+examples找到它
classdef(Enumeration) DmatFileMode < int32
enumeration
ReadMode(0)
ReadCompatibilityMode(1)
WriteMode(2)
end
<snip>
end
【讨论】:
您还可以使用 Matlab 代码中的 Java 枚举类。在 Java 中定义它们并将它们放在 Matlab 的 javaclasspath 中。
// Java class definition
package test;
public enum ColorEnum {
RED, GREEN, BLUE
}
您可以在 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
不过,它不会捕捉到与其他类型的比较。并且与字符串的比较有一个奇怪的返回大小。
>> test.ColorEnum.RED == 'GREEN'
ans =
0
>> test.ColorEnum.RED == 'RED'
ans =
1 1 1
【讨论】:
您可以创建一个行为类似于Java's old typesafe enum pattern 的 Matlab 类。 Marc's solution 的修改可以将它从 C 风格的 typedef 变成更像 Java 风格的类型安全枚举。在这个版本中,常量中的值是类型化的 Color 对象。
优点:
缺点:
总的来说,我不知道哪种方法更好。在实践中都没有使用过。
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
这是一个锻炼它的功能。
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
使用示例:
>> 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
>>
这两种方法都有一个小问题:将常量放在“==”左侧以防止错误分配的 C 约定在这里没有多大帮助。在 Matlab 中,如果您不小心在 LHS 上将此常量与“=”一起使用,它只会创建一个名为 Colors 的新局部结构变量,并且会屏蔽枚举类。
>> Colors.BLUE = 42
Colors =
BLUE: 42
>> Color.BLUE = 42
Color =
BLUE: 42
>> Color.RED
??? Reference to non-existent field 'RED'.
【讨论】:
如果您有权访问统计工具箱,则可以考虑使用categorical object。
【讨论】:
在尝试了此页面上的其他建议后,我选择了 Andrew 的完全面向对象的方法。非常好 - 谢谢安德鲁。
但是,如果有人感兴趣,我做了(我认为是)一些改进。特别是,我不再需要重复指定枚举对象的名称。这些名称现在是使用反射和元类系统派生的。此外,重新编写了 eq() 和 ismember() 函数,以便为枚举对象的矩阵返回形状正确的返回值。最后,修改了 check_type_safety() 函数,使其与包目录(例如命名空间)兼容。
看起来效果不错,但请告诉我你的想法:
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
谢谢, 梅森
【讨论】:
Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};
Toys{3}
ans = 'Rex'
【讨论】:
如果您需要枚举类型只是为了传递给 C# 或 .NET 程序集, 您可以使用 MATLAB 2010 构造和传递枚举:
A = NET.addAssembly(MyName.dll)
% suppose you have enum called "MyAlerts" in your assembly
myvar = MyName.MyAlerts.('value_1');
您也可以在以下位置查看 MathWorks 的官方答案
// the enum "MyAlerts" in c# will look something like this
public enum MyAlerts
{
value_1 = 0,
value_2 = 1,
MyAlerts_Count = 2,
}
【讨论】: