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

发布于 2024-08-04 05:57:21 字数 36 浏览 9 评论 0 原文

MATLAB 中有枚举类型吗?如果没有,还有哪些替代方案?

Are there enumerated types in MATLAB? If not, what are the alternatives?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

热鲨 2024-08-11 05:57:21

从 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

Starting from R2010b, MATLAB supports enumerations.

Example from the 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
花心好男孩 2024-08-11 05:57:21

您可以使用新型 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,但不幸的是语法更加笨拙。

You can get some of the functionality with new-style MATLAB classes:

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

This isn't really a type, but since MATLAB is loosely typed, if you use integers, you can do things that approximate it:

line1 = Colors.RED;
...
if Colors.BLUE == line1
end

In this case, MATLAB "enums" are close to C-style enums - substitute syntax for integers.

With the careful use of static methods, you can even make MATLAB enums approach Ada's in sophistication, but unfortunately with clumsier syntax.

暖心男生 2024-08-11 05:57:21

如果您想做一些类似于 Marc 建议的事情,您只需制作一个 结构 来表示您的枚举类型而不是一个全新的类:

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

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

a = colors.RED;

或者可以使用 动态字段名称(如果您在字符串中包含字段名称):

a = colors.('RED');

事实上,按照 Marc 建议并创建一个全新的类来表示“枚举”对象有一些好处

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

但是,如果您不需要这种复杂性而只需要快速执行某些操作,那么结构可能是最简单、最直接的实现。它还可以与不使用最新 OOP 框架的旧版本 MATLAB 配合使用。

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);

One benefit is that you can easily access structures in two different ways. You can specify a field directly using the field name:

a = colors.RED;

or you can use dynamic field names if you have the field name in a string:

a = colors.('RED');

In truth, there are a few benefits to doing what Marc suggested and creating a whole new class to represent an "enum" object:

  • You can control how the object is modified.
  • You can keep the definition in one place and easily use it in multiple places.
  • You can control failures and make them more "graceful", like returning an empty matrix if you try to access a non-existent field (as opposed to throwing an error).

However, if you don't need that sort of complexity and just need to do something quick, a structure is likely the easiest and most straight-forward implementation. It will also work with older versions of MATLAB that don't use the newest OOP framework.

怼怹恏 2024-08-11 05:57:21

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

You could also use Java enum classes from your Matlab code. Define them in Java and put them on your Matlab's javaclasspath.

// Java class definition
package test;
public enum ColorEnum {
    RED, GREEN, BLUE
}

You can reference them by name in 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

It won't catch comparisons to other types, though. And comparison to string has an odd return size.

>> test.ColorEnum.RED == 'GREEN'
ans =
     0
>> test.ColorEnum.RED == 'RED'
ans =
     1     1     1
淡紫姑娘! 2024-08-11 05:57:21

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

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

classdef(Enumeration) DmatFileMode < int32

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

There is actually a keyword in MATLAB R2009b called 'enumeration'. It seems to be undocumented, and I cannot say I know how to use it, but the functionality is probably there.

You can find it in matlabroot\toolbox\distcomp\examples\+examples

classdef(Enumeration) DmatFileMode < int32

    enumeration
        ReadMode(0)
        ReadCompatibilityMode(1)
        WriteMode(2)
    end
<snip>
end
莫相离 2024-08-11 05:57:21

你可以创建一个Matlab类,其行为类似于Java的旧类类型安全枚举模式。对Marc的解决方案进行修改可以接受它从 C 风格的 typedef 到更像 Java 风格的类型安全枚举。在此版本中,常量中的值是类型化的 Color 对象。

优点:

  • 可以通过 == 和其他操作检查类型(在运行时),以防止与原始数字或其他类型的枚举的意外比较。
  • 您可以显式检查变量的类型(在运行时)。
  • 值以可读名称显示,而不是不透明的代码。
  • 不允许像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 中,如果您不小心在 LHS 上使用“=”与此常量,它只会创建一个名为 Colors 的新局部结构变量,而不是出现错误,并且会屏蔽枚举类。

>> Colors.BLUE = 42
Colors = 
    BLUE: 42
>> Color.BLUE = 42
Color = 
    BLUE: 42
>> Color.RED
??? Reference to non-existent field 'RED'.

You could make a Matlab class that behaves like a Java's old typesafe enum pattern. A modification of Marc's solution could take it from C-style typedefs to more like Java-style typesafe enums. In this version, the values in the constants are typed Color objects.

The upsides:

  • The type can be checked (at runtime) by == and other operations to prevent accidental comparison to raw numerics or other types of enums.
  • You can explicitly check the type of your variables (at runtime).
  • Values are displayed with readable names instead of the opaque codes.
  • Operations like mean() and std() that don't make sense on enums are disallowed.

Downsides:

  • Longer class definition. But, this is all boilerplate, and can be reused for any other enum class, changing just the class name and Constant properties.
  • These enums cannot be used directly in switch blocks. Need to pop the Code out, which loses some type safety.
  • Objects will be slower than primitives. Relevant if you're using constants inside loops.

On the whole, I don't know which approach is better. Haven't used either in practice.

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

Here's a function to exercise it.

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

Example of use:

>> 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

>> 

A minor quirk in both approaches: the C convention of putting the constant on the left hand of the "==" to prevent bad assignment doesn't help as much here. In Matlab, if you accidentally use "=" with this constant on the LHS, instead of an error, it'll just create a new local struct variable named Colors, and it will mask the enum class.

>> Colors.BLUE = 42
Colors = 
    BLUE: 42
>> Color.BLUE = 42
Color = 
    BLUE: 42
>> Color.RED
??? Reference to non-existent field 'RED'.
初心 2024-08-11 05:57:21

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

If you have access to the Statistics Toolbox, you might consider using a categorical object.

傲鸠 2024-08-11 05:57:21

在尝试了本页上的其他建议后,我采用了 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

谢谢,
石匠

After trying out the other suggestions on this page, I landed on Andrew's fully object-oriented approach. Very nice - thanks Andrew.

In case anyone is interested, however, I made (what I think are) some improvements. In particular, I removed the need to double-specify the name of the enum object. The names are now derived using reflection and the metaclass system. Further, the eq() and ismember() functions were re-written to give back properly-shaped return values for matrices of enum objects. And finally, the check_type_safety() function was modified to make it compatible with package directories (e.g. namespaces).

Seems to work nicely, but let me know what you think:

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

Thanks,
Mason

醉梦枕江山 2024-08-11 05:57:21
Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};

Toys{3}
  ans = 'Rex'
Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};

Toys{3}
  ans = 'Rex'
我不吻晚风 2024-08-11 05:57:21

如果您需要枚举类型只是为了传递给 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,
}

If you need the enumerated types just for passing to C# or .NET assembly,
you can construct and pass the enums with MATLAB 2010:

A = NET.addAssembly(MyName.dll)
% suppose you have enum called "MyAlerts" in your assembly
myvar = MyName.MyAlerts.('value_1');

you can also check the official MathWorks answer at

How do I use .NET enumerated values in 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,
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文