如何在MATLAB中创建枚举类型?

44

Matlab中是否有枚举类型?如果没有,有什么替代方案?

10个回答

44

从R2010b版本开始,MATLAB支持枚举。

以下是来自文档的示例:

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

6
给未来读者,这可能应该是被接受的答案(尽管所有好的解决方案都不错)。 - Amro
2
这是一个更复杂的枚举类示例。文档中可以找到一个更简单的示例。 - James Mertz
3
@KronoS提到的更简单的示例的链接: link - JaBe

28

您可以通过新式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的“enums”与C风格的枚举(non)相似-替代整数的语法。
通过谨慎使用静态方法,甚至可以使MATLAB的枚举接近于Ada的成熟度,但不幸的是,其语法更加笨拙。

当使用新的面向对象的东西时,请注意可能会出现性能问题。根据我的经验,它引入了显着的开销。然而,这真的取决于你正在做什么。 - Benjamin Oakes
5
实际上,对于简单的类,与使用大量全局结构相比,几乎没有时间惩罚。然而,采用良好实践节省的开发时间是相当可观的,而且很少有运行时间成为问题的情况。硬件很便宜,编写软件的人不便宜。 - Marc
现在标准的枚举关键字已经出现了,这是更好的答案。 - Marc

18
如果你想做类似于Marc所建议的事情,你可以简单地创建一个表示枚举类型的结构体,而不是一个全新的类:

If you want to do something similar to what Marc suggested, you could simply make a structure to represent your enumerated types instead of a whole new class:

colors = struct('RED', 1, 'GREEN', 2, 'BLUE', 3);

其中一个好处是可以通过两种不同的方式轻松访问结构。您可以使用字段名称直接指定字段:

a = colors.RED;

如果你有一个字段名字的字符串,也可以使用动态字段名

a = colors.('RED');

实际上,按照Marc的建议创建一个全新的类来表示“枚举”对象具有以下几个好处:

  • 您可以控制对象的修改。
  • 您可以在一个地方保留定义并轻松地在多个地方使用它。
  • 您可以控制错误并使其更加“优雅”,例如如果尝试访问不存在的字段,则返回空矩阵(而不是抛出错误)。

但是,如果您不需要那种复杂性,只需要快速完成某些操作,则结构体可能是最简单和最直接的实现。它也适用于不使用最新OOP框架的旧版本MATLAB。


8

实际上,在MATLAB R2009b中有一个关键字叫做'enumeration'。它似乎没有文档记录,我也不知道如何使用它,但功能可能是存在的。

你可以在matlabroot\toolbox\distcomp\examples\+examples中找到它。

classdef(Enumeration) DmatFileMode < int32

    enumeration
        ReadMode(0)
        ReadCompatibilityMode(1)
        WriteMode(2)
    end
<snip>
end

1
如果你要进行代码生成,那么这是正确的做法。在Simulink文档中更好地记录在“定义枚举数据类型”下。 - Patrick O'Leary

7
您还可以在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

5
你可以创建一个Matlab类,使其像Java的旧类型安全枚举模式一样运作。 Marc's解决方案的修改可以将其从C风格的typedef转换为更类似于Java风格的类型安全枚举。在此版本中,常量中的值被归类为颜色对象。
优点:
- 可以通过==等操作(在运行时)检查类型,以防止意外比较原始数值或其他类型的枚举。 - 你可以在运行时显式检查变量的类型。 - 值使用可读名称显示,而不是不透明代码。 - 不允许在枚举上执行不合理的操作,如mean()和std()。
缺点:
  • 类定义更长。但这只是样板,可以重用于任何其他枚举类,只需更改类名和常量属性。
  • 这些枚举不能直接在 switch 块中使用。需要弹出代码,这会导致一些类型安全性丢失。
  • 对象比原始类型慢。如果您在循环内部使用常量,则相关。

总的来说,我不知道哪种方法更好。没有在实践中使用过。

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中,如果您在常量的左侧意外使用“=”,它不会报错,而是会创建一个名为Colors的新本地结构变量,并隐藏枚举类。
>> Colors.BLUE = 42
Colors = 
    BLUE: 42
>> Color.BLUE = 42
Color = 
    BLUE: 42
>> Color.RED
??? Reference to non-existent field 'RED'.

4
如果您有访问统计工具箱的权限,可以考虑使用 分类对象

3

在尝试了本页面上的其他建议后,我采用了安德鲁的完全面向对象方法。非常好-谢谢安德鲁。

如果有人感兴趣,我进行了一些改进(我认为是)。特别是,我消除了需要双重指定枚举对象名称的需要。现在使用反射和元类系统来派生名称。此外,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

感谢您,Mason。

2
Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};

Toys{3}
  ans = 'Rex'

1
如果您只需要将枚举类型传递给C#或.NET程序集,您可以使用MATLAB 2010构建并传递枚举。
A = NET.addAssembly(MyName.dll)
% suppose you have enum called "MyAlerts" in your assembly
myvar = MyName.MyAlerts.('value_1');

您还可以查看MathWorks官方答案,网址为

如何在MATLAB 7.8(R2009a)中使用.NET枚举值?

// the enum "MyAlerts" in c# will look something like this
public enum MyAlerts
{
    value_1 = 0,
    value_2 = 1,
    MyAlerts_Count = 2,
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接