【问题标题】:How do I create enumerated types in MATLAB?如何在 MATLAB 中创建枚举类型?
【发布时间】:2010-11-26 05:15:22
【问题描述】:

MATLAB 中有枚举类型吗?如果没有,有什么替代方案?

【问题讨论】:

    标签: matlab enums matlab-class


    【解决方案1】:

    从 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
    

    【讨论】:

    • 对于未来的读者来说,这可能应该是公认的答案(尽管如此,所有好的解决方案)
    • 这是一个更复杂的枚举类示例。可以在文档中找到更简单的。
    • @KronoS 提到的更简单示例的 URL:link
    【解决方案2】:

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

    【讨论】:

    • 在使用新的面向对象的东西时要注意可能的性能损失。根据我的经验,它引入了巨大的开销。然而,这真的取决于你在做什么。
    • 实际上,对于简单的类,与使用大量全局结构相比,实际上没有时间损失。然而,良好实践所节省的开发时间是可观的,而且很少出现运行时这样的问题。硬件便宜。编写软件的人不是。
    • 现在标准枚举关键字见下文。这是现在更好的答案。
    【解决方案3】:

    如果您想做一些与Marc 建议的类似的事情,您可以简单地创建一个structure 来表示您的枚举类型,而不是一个全新的类:

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

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

    a = colors.RED;
    

    或者,如果您在字符串中包含字段名称,则可以使用 dynamic field names

    a = colors.('RED');
    

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

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

    但是,如果您不需要那种复杂性并且只需要快速执行某项操作,那么结构可能是最简单、最直接的实现。它也适用于不使用最新 OOP 框架的旧版 MATLAB。

    【讨论】:

      【解决方案4】:

      其实在 MATLAB R2009b 中有一个关键字叫做'enumeration'。它似乎没有记录,我不能说我知道如何使用它,但功能可能就在那里。

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

      classdef(Enumeration) DmatFileMode < int32
      
          enumeration
              ReadMode(0)
              ReadCompatibilityMode(1)
              WriteMode(2)
          end
      <snip>
      end
      

      【讨论】:

      • 如果您要执行代码生成,这是正确的方法。它在 Simulink 文档中的“定义枚举数据类型”下有更好的记录。
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        您可以创建一个行为类似于Java's old typesafe enum pattern 的 Matlab 类。 Marc's solution 的修改可以将它从 C 风格的 typedef 变成更像 Java 风格的类型安全枚举。在这个版本中,常量中的值是类型化的 Color 对象。

        优点:

        • 可以通过 == 和其他操作(在运行时)检查类型,以防止意外与原始数字或其他类型的枚举进行比较。
        • 您可以显式检查变量的类型(在运行时)。
        • 值以可读的名称而不是不透明的代码显示。
        • 不允许对枚举没有意义的 mean() 和 std() 等操作。

        缺点:

        • 更长的类定义。但是,这都是样板文件,可以重复用于任何其他枚举类,只需更改类名和常量属性。
        • 这些枚举不能直接在开关块中使用。需要弹出代码,这会失去一些类型安全性。
        • 对象将比基元慢。如果您在循环内使用常量,则相关。

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

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

        【讨论】:

          【解决方案7】:

          如果您有权访问统计工具箱,则可以考虑使用categorical object

          【讨论】:

            【解决方案8】:

            在尝试了此页面上的其他建议后,我选择了 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
            

            谢谢, 梅森

            【讨论】:

              【解决方案9】:
              Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};
              
              Toys{3}
                ans = 'Rex'
              

              【讨论】:

                【解决方案10】:

                如果您需要枚举类型只是为了传递给 C# 或 .NET 程序集, 您可以使用 MATLAB 2010 构造和传递枚举:

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

                您也可以在以下位置查看 MathWorks 的官方答案

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

                【讨论】:

                  猜你喜欢
                  • 2015-02-10
                  • 2012-09-23
                  • 1970-01-01
                  • 2017-08-19
                  • 1970-01-01
                  • 2020-03-03
                  • 2022-01-09
                  • 2021-03-17
                  相关资源
                  最近更新 更多